kk Blog —— 通用基础

date [-d @int|str] [+%s|"+%F %T"]

在64位主机上编译产生32位的目标代码

64位平台跟32位平台有很大的不同,包括参数传递方式,指令集都有很大的变化,那怎么能够让它正常运行呢?利用 gcc的-m32参数编译产生32位的目标代码,而不是64位的目标代码,因为32位的目标代码可以运行在64位的主机上。

1
2
3
4
5
6
$ gcc -m32 manydots.s -o manydots
$ ./manydots 
How many dots do you want to see? 10
..........
$ file manydots
manydots: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.8, not stripped

可以看到,这样就okay了。
实际上,我们还可以分步来做:先汇编,后链接。这样可以减少目标代码的大小,先看看原来的大小。

1
2
$ wc -c manydots
6495 manydots

我们分步汇编、链接:

1
2
3
4
5
6
7
8
9
10
11
// 这个时候是需要一个默认的_start入口的,如果不指定,会默认设置一个程序入口地址,因为这个时候没有人给我们设置一个真正的入口_start了。
$ sed -i -e "s/main/_start/g" manydots.s 
$ as --32 manydots.s -o manydots.o
$ ld -m elf_i386 manydots.o -o manydots
$ wc -c manydots
1026 manydots
$ echo "6495-1026" | bc 
5469
$ ./manydots 
How many dots do you want to see? 10
..........

可以发现,这样也可以正常工作,不过目标减少了5469个字节。为什么会有这样的效果呢?资料[2]给出了详细的解释,如果感兴趣,可以研究一下。
对了,“as –32 manydots.s -o manydots.o”可以直接用“$ gcc -m32 -c manydots.s -o manydots.o” 来做,他们两个实际上做了同一个事情,你可以通过gcc的–verbose查看:

1
2
3
4
5
6
7
8
9
10
11
12
$ gcc --verbose -m32 -c manydots.s -o manydots.o
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Debian 4.3.1-9' --with-bugurl=file:///usr/share/doc/gcc-4.3/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --enable-shared --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --enable-nls --with-gxx-include-dir=/usr/include/c++/4.3 --program-suffix=-4.3 --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --enable-mpfr --enable-cld --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.3.1 (Debian 4.3.1-9) 
COLLECT_GCC_OPTIONS='-v' '-m32' '-c' '-o' 'manydots.o' '-mtune=generic'
 as -V -Qy --32 -o manydots.o manydots.s
GNU assembler version 2.18.0 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.18.0.20080103
COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/
LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.3.1/32/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/32/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/../../../../lib32/:/lib/../lib32/:/usr/lib/../lib32/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/:/usr/lib/gcc/x86_64-linux-gnu/4.3.1/../../../:/lib/:/usr/lib/
COLLECT_GCC_OPTIONS='-v' '-m32' '-c' '-o' 'manydots.o' '-mtune=generic'

最后总结一下,在64位主机上编译产生32位目标代码的办法:

一、办法一:直接通过gcc汇编、链接

1、确保不要有重复的start入口,把start替换成main
2、用gcc加上-m32参数进行汇编和链接

二、办法二:分步汇编、链接

1、汇编的时候,用gcc加上-m32参数或者用as加上–32参数。
2、在链接的时候,用ld加上-m elf_i386参数。

64位汇编参数传递

64位汇编

当参数少于7个时, 参数从左到右放入寄存器: rdi, rsi, rdx, rcx, r8, r9。
当参数为7个以上时, 前 6 个与前面一样, 但后面的依次从 “右向左” 放入栈中,即和32位汇编一样。

参数个数大于 7 个的时候
H(a, b, c, d, e, f, g, h);
a->%rdi, b->%rsi, c->%rdx, d->%rcx, e->%r8, f->%r9
h->8(%esp)
g->(%esp)
call H


Linux (and Windows) x86-64 calling conventionhas the first few arguments noton the stack, but in registers instead
See http://www.x86-64.org/documentation/abi.pdf (page 20)
Specifically:
If the class is MEMORY, pass the argument on the stack.
If the class is INTEGER, the next available register of the sequence %rdi, %rsi, %rdx, %rcx, %r8 and %r9 is used.
If the class is SSE, the next available vector register is used, the registers are taken in the order from %xmm0 to %xmm7.
If the class is SSEUP, the eightbyte is passed in the next available eightbyte chunk of the last used vector register.
If the class is X87, X87UP or COMPLEX_X87, it is passed in memory.
The INTEGERclass is anything that will fit in a general purpose register


【x86_64 Assembler Calling Convention】

1、x86_64 registers

2、x86_64寄存器特性表

3、特性要点:

  1)常用寄存器有16个,分为x86通用寄存器以及r8-r15寄存器。
  2)通用寄存器中,函数执行前后必须保持原始的寄存器有3个:是rbx、rbp、rsp。rx寄存器中,最后4个必须保持原值:r12、r13、r14、r15。
保持原值的意义是为了让当前函数有可信任的寄存器,减小在函数调用过程中的保存&恢复操作。除了rbp、rsp用于特定用途外,其余5个寄存器可随意使用。
  3)通用寄存器中,不必假设保存值可随意使用的寄存器有5个:是rax、rcx、rdx、rdi、rsi。其中rax用于第一个返回寄存器(当 然也可以用于其它用途),rdx用于第二个返回寄存器(在调用函数时也用于第三个参数寄存器)。rcx用于第四个参数。rdi用于第一个参数。rsi用于 第二个函数参数。
  4)r8、r9分配用于第5、第6个参数。

两个数只出现一次

problem

一个数组,其中只有两个数只出现一次,其余数皆出现偶数次。
设计Time: O(n) 的算法得出那个只出现一次的数。

answer

再因为a和b不等,那么ab必然不为0。
那么ab这个数上面必然能够找到一个二进制位是1
在这个二进制位上,a和b不等。
根据这个二进制位,将各元素中在这位上为1的分派到左边,为0的分派到右边,形成两个子数组。

可以证明,
1)这两个数组分别包含a和b。
2)每个数组中除了a或b之外的所有元素都是成对出现的。

eclipse生成jar包

第一:普通类导出jar包

普通类就是指此类包含main方法,并且没有用到别的jar包。
1.在eclipse中选择你要导出的类或者package,右击,选择Export子选项;
2.在弹出的对话框中,选择java文件—选择JAR file,单击next;
3.在JAR file后面的文本框中选择你要生成的jar包的位置以及名字,注意在Export generated class files and resources和Export java source files and resources前面打上勾,单击next;
4.单击两次next按钮,到达JAR Manifest Specification。注意在最底下的Main class后面的文本框中选择你的jar包的入口类。单击Finish,完成。

运行 java -jar 名字.jar,检测运行是否正确。

第二、你所要导出的类里边用到了别的jar包。

比如说你写的类连接了数据库,用到数据库驱动包oracl.jar.。
1.先把你要导出的类按照上面的步骤导出形成jar包,比如叫test.jar
2.新建一个文件夹main,比如在D盘根目录下;
3.把test.jar和oracl.jar拷贝到main文件下,右击test.jar,解压到当前文件夹。把META-INF\MANIFEST.MF剪切到另外一个地方 (比如是桌面!) ;
4.右击oracl.jar,解压到当前文件夹。
5.在dos环境下,进入到D盘的main文件夹下,执行 jar cvfm new.jar meta-inf/manifest.mf .,不要忘了最后面的点。
6.用压缩工具打开你新生成的new.jar,用你放在桌面的META-INF\MANIFEST.MF覆盖new.jar原有。

运行 java -jar 名字.jar,检测运行是否正确。

统计git提交行数的脚本

可以保存为count.sh运行 ./count.sh your_name

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/bin/sh
insert=0
delete=0
git log--author=$1--shortstat--pretty=format:""|sed/^$/d >.tmp.count

whilereadline ;do
current=`echo$line|awk-F',''{printf $2}'|awk'{printf $1}'`
insert=`expr$insert+ $current`
current=`echo$line|awk-F',''{printf $3}'|awk'{printf $1}'`
delete=`expr$delete+ $current`
done<.tmp.count

rm .tmp.count
echo$1$insertinsertions, $deletedeletions