正文
《Linux C编程一站式学习》 是宋劲杉写的一本关系C语言学习的好书,充分考虑到了学以致用的环境Linux。 看了好多C语言的书,包括谭浩强的那本,但还是无法下手编程,不能解决实际操作问题,另外感觉只是翻书, 始终不了解C代码是怎么操作计算机底层的,查来查去说道要想知道C语言是怎样与计算机底层交互的, 就需要去把这几本书翻一遍:编译原理、操作系统、计算机组成原理、数据结构、数据库、网络、软件工程 等。 这里面哪一本不是砖头厚?听名字都要后退3步。所以一直想能找到一本领进门的书,这本书可以使人更透彻的学习。 偶然逛知乎碰到了《Linux C编程一站式学习》,翻了几天,被这本书打动了,这就是我一直在找的那本书, 入门浅显、知识点覆盖全面、知其然还能知其所以然,而且还是开源的。这里就是一些知识点记录一下,以备查阅翻看。
gdb http://akaedu.github.io/book/ch10.html
Linux 家族历史:
计算机内核运行模式:
c运算符优先级
-
标识符、 常量、 字符串 和 用()括号套起来的表达式 是组成表达式的最基本单元,在运算中做操作数,优先级最高。
-
后缀运算符,包括数组取下标
[]
、 函数调用()
、 结构体取成员.
、 指向结构体的指针取成员->
、后缀自增++
、后缀自减--
。 如果一个操作数后面有多个后缀,按照离操作数从近到远的顺序(也就是从左到右)依次计算, 比如 a.name++ ,先算 a.name ,再++,这里的 .name 应该看成 a 的一个后缀,而不是把 . 看成双目运算符。 -
单目运算符,包括
++前缀自增
、--前缀自减
、sizeof
、 类型转换()
、 取地址运算&
、 指针间接寻址*
、正号+
、负号-
、按位取反~
、逻辑非!
。 如果一个操作数前面有多个前缀,按照离操作数从近到远的顺序(也就是从右到左)依次计算,比如 !~a ,先算 ~a ,再求!。 -
乘*、除/、模%运算符。 这三个运算符是左结合的。
-
加+、减-运算符。左结合。
-
移位运算符«和»。左结合。
-
关系运算符< > <= >=。左结合。
-
相等性运算符==和!=。左结合。
-
按位与
&
。左结合。 -
按位异或
^
。左结合。 -
按位或
|
。左结合。 -
逻辑与
&&
。左结合。 -
逻辑或
||
。左结合。 -
条件运算符
:?
。在第 2 节 “if/else语句”讲过Dangling-else问题,条件运算符也有类似的问题。 例如 a ? b : c ? d : e 是看成 (a ? b : c) ? d : e 还是 a ? b : (c ? d : e) 呢?C语言规定是后者。 -
赋值
=
和 各种复合赋值*= /= %= += -= <<= >>= &= ^= |=
。 在双目运算符中只有赋值和复合赋值是右结合的。 -
逗号运算符
,
。 左结合。
指向指针的指针
指针可以指向复合类型。
指针也可以指向指针自己,看一个例子:
#include <stdio.h>
int main(void)
{
int i;
int *pi = &i;
int **ppi = π
printf(" &i=%p\n pi=%p\n &pi=%p\n ppi=%p\n *ppi=%p\n", &i, pi, &pi, ppi, *ppi);
return 0;
}
输出:
&i=0x7ffdcce13ed4
pi=0x7ffdcce13ed4
&pi=0x7ffdcce13ec8
ppi=0x7ffdcce13ec8
*ppi=0x7ffdcce13ed4
可以看出,*
只是一个标示,说明这个变量保存的是另一个标量的地址,如ppi保存的是pi的地址,*ppi
意思是读取ppi保存的值也就是pi地址上的保存的值,即i的地址。
可以看出*(&i) = i
, 分析下**ppi
: **ppi = *(*ppi) = *(*(&pi)) = *pi = *(&i) = i
,从里面也看到了*ppi = *(&pi) = pi = &i
。
指向数组的指针
先看一下 typedef 的使用。
C标准规定 size_t 是一种无符号整型,编译器可以用 typedef 做一个类型声明:
typedef unsigned long size_t;
那么 size_t 就代表 unsigned long 型。不同平台的编译器可能会根据自己平台的具体情况定义 size_t 所代表的类型, 比如有的平台定义为 unsigned long 型,有的平台定义为 unsigned long long 型,C标准规定 size_t 这个名字就是为了隐藏这些细节, 使代码具有可移植性。
所以注意不要把 size_t 类型和它所代表的真实类型混用,例如:
unsigned long x;
size_t y;
x = y;
如果在一种ILP32平台上定义 size_t 代表 unsigned long long 型,这段代码把 y 赋给 x 时就把高位截掉了,结果可能是错的。
typedef 这个关键字用于给某种类型起个新名字,比如上面的 typedef 声明可以这么看:去掉 typedef 就成了一个变量声明 unsigned long size_t;
,
size_t 是一个变量名,类型是 unsigned long ,那么加上 typedef 之后, size_t 就是一个类型名,就代表 unsigned long 类型。
再举个例子:
typedef char array_t[10];
array_t a;
这相当于声明 char a[10];
。 类型名也遵循标识符的命名规则,并且通常加个 _t 后缀表示Type。
定义一个指向数组的指针,该数组有10个int元素:
int (*a)[10];
和指针数组的定义int *a[10];
相比,仅仅多了一个()括号。如何记住和区分这两种定义呢?
我们可以认为[]
比*
有更高的优先级,如果a先和*
结合则表示a是一个指针,如果a先和[]
结合则表示a是一个数组。
int *a[10];
这个定义可以拆成两句:
typedef int *t;
t a[10];
t代表int *
类型,a则是由这种类型的元素组成的数组。
int (*a)[10];
这个定义也可以拆成两句:
typedef int t[10];
t *a;
t代表由10个int组成的数组类型,a则是指向这种类型的指针。
现在看指向数组的指针如何使用:
int a[10];
int (*pa)[10] = &a;
a是一个数组,在&a
这个表达式中,数组名做左值,取整个数组的首地址赋给指针pa。注意,&a[0]
表示数组a的首元素的首地址,而&a
表示数组a的首地址,
显然这两个地址的数值相同,但这两个表达式的类型是两种不同的指针类型,前者&a[0]
的类型是int *
,而后者&a
的类型是int (*)[10]
。
*pa
就表示pa所指向的数组a,所以取数组的a[0]
元素可以用表达式(*pa)[0]
。 注意到*pa
可以写成pa[0]
,
所以(*pa)[0]
这个表达式也可以改写成pa[0][0]
,pa就像一个二维数组的名字,它表示什么含义呢?下面把pa和二维数组放在一起做个分析。
int a[5][10];
和int (*pa)[10];
之间的关系同样类似于int a[10];
和int *pa;
之间的关系:a是由一种元素组成的数组,pa则是指向这种元素的指针。
所以,如果pa指向a的首元素:
int a[5][10];
int (*pa)[10] = &a[0];
则pa[0]
和a[0]
取的是同一个元素,唯一比原来复杂的地方在于这个元素是由10个int组成的数组,而不是基本类型。
这样,我们可以把pa当成二维数组名来使用,pa[1][2]
和a[1][2]
取的也是同一个元素,而且pa比a用起来更灵活,数组名不支持赋值、自增等运算,
而指针可以支持,pa++使pa跳过二维数组的一行(40个字节),指向a[1]
的首地址。
或者换个角度想一下,数组的本质就是指针,一维数组 int a【10】; int *pa = &a[0];
, 数组名做右值时自动转换成指向首元素的指针,
int *pa = &a[0]; => int *pa = a;
,可以看出pa就是a,*pa = *a = *(&a[0]) = a[0]
,
取a[2]
的值有 a[2] = *(pa + 2) = *(&a[0] + 2) = *(a + 2)
。
a[2]
和 pa[2]
本质上是一样的,都是通过指针间接寻址访问元素。
一维数组用指针可以运算,如果二维数组每一行的第1个位置都有指针指向,那二维数组运算起来会方便很多,而这个正是上面说的指向数组的指针, 指针加1就是移到下一行的第1个位置。三维数组也类似。
链表
linkedlist.h
/* linkedlist.h */
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
typedef struct node *link;
struct node {
unsigned char item;
link next;
};
link make_node(unsigned char item);
void free_node(link p);
link search(unsigned char key);
void insert(link p);
void delete(link p);
void traverse(void (*visit)(link));
void destroy(void);
void push(link p);
link pop(void);
#endif
linkedlist.c
/* linkedlist.c */
#include <stdlib.h>
#include "linkedlist.h"
static link head = NULL;
link make_node(unsigned char item)
{
link p = malloc(sizeof *p);
p->item = item;
p->next = NULL;
return p;
}
void free_node(link p)
{
free(p);
}
link search(unsigned char key)
{
link p;
for (p = head; p; p = p->next)
if (p->item == key)
return p;
return NULL;
}
void insert(link p)
{
p->next = head;
head = p;
}
void delete(link p)
{
link pre;
if (p == head) {
head = p->next;
return;
}
for (pre = head; pre; pre = pre->next)
if (pre->next == p) {
pre->next = p->next;
return;
}
}
/* delete方法改进 */
void delete(link p)
{
link *pnext;
for (pnext = &head; *pnext; pnext = &(*pnext)->next)
if (*pnext == p) {
*pnext = p->next;
return;
}
}
void traverse(void (*visit)(link))
{
link p;
for (p = head; p; p = p->next)
visit(p);
}
void destroy(void)
{
link q, p = head;
head = NULL;
while (p) {
q = p;
p = p->next;
free_node(q);
}
}
void push(link p)
{
insert(p);
}
link pop(void)
{
if (head == NULL)
return NULL;
else {
link p = head;
head = head->next;
return p;
}
}
main.c
/* main.c */
#include <stdio.h>
#include "linkedlist.h"
void print_item(link p)
{
printf("%d\n", p->item);
}
int main(void)
{
link p = make_node(10);
insert(p);
p = make_node(5);
insert(p);
p = make_node(90);
insert(p);
p = search(5);
delete(p);
free_node(p);
traverse(print_item);
destroy();
p = make_node(100);
push(p);
p = make_node(200);
push(p);
p = make_node(250);
push(p);
while (p = pop()) {
print_item(p);
free_node(p);
}
return 0;
}
双向链表
doublylinkedlist.h
/* doublylinkedlist.h */
#ifndef DOUBLYLINKEDLIST_H
#define DOUBLYLINKEDLIST_H
typedef struct node *link;
struct node {
unsigned char item;
link prev, next;
};
link make_node(unsigned char item);
void free_node(link p);
link search(unsigned char key);
void insert(link p);
void delete(link p);
void traverse(void (*visit)(link));
void destroy(void);
void enqueue(link p);
link dequeue(void);
#endif
doublylinkedlist.c
/* doublylinkedlist.c */
#include <stdlib.h>
#include "doublylinkedlist.h"
struct node tailsentinel;
struct node headsentinel = {0, NULL, &tailsentinel};
struct node tailsentinel = {0, &headsentinel, NULL};
static link head = &headsentinel;
static link tail = &tailsentinel;
link make_node(unsigned char item)
{
link p = malloc(sizeof *p);
p->item = item;
p->prev = p->next = NULL;
return p;
}
void free_node(link p)
{
free(p);
}
link search(unsigned char key)
{
link p;
for (p = head->next; p != tail; p = p->next)
if (p->item == key)
return p;
return NULL;
}
void insert(link p)
{
p->next = head->next;
head->next->prev = p;
head->next = p;
p->prev = head;
}
void delete(link p)
{
p->prev->next = p->next;
p->next->prev = p->prev;
}
void traverse(void (*visit)(link))
{
link p;
for (p = head->next; p != tail; p = p->next)
visit(p);
}
void destroy(void)
{
link q, p = head->next;
head->next = tail;
tail->prev = head;
while (p != tail) {
q = p;
p = p->next;
free_node(q);
}
}
void enqueue(link p)
{
insert(p);
}
link dequeue(void)
{
if (tail->prev == head)
return NULL;
else {
link p = tail->prev;
delete(p);
return p;
}
}
main.c
/* main.c */
#include <stdio.h>
#include "doublylinkedlist.h"
void print_item(link p)
{
printf("%d\n", p->item);
}
int main(void)
{
link p = make_node(10);
insert(p);
p = make_node(5);
insert(p);
p = make_node(90);
insert(p);
p = search(5);
delete(p);
free_node(p);
traverse(print_item);
destroy();
p = make_node(100);
enqueue(p);
p = make_node(200);
enqueue(p);
p = make_node(250);
enqueue(p);
while (p = dequeue()) {
print_item(p);
free_node(p);
}
return 0;
}
二叉树
binarytree.h
/* binarytree.h */
#ifndef BINARYTREE_H
#define BINARYTREE_H
typedef struct node *link;
struct node {
unsigned char item;
link l, r;
};
link init(unsigned char VLR[], unsigned char LVR[], int n);
void pre_order(link t, void (*visit)(link));
void in_order(link t, void (*visit)(link));
void post_order(link t, void (*visit)(link));
int count(link t);
int depth(link t);
void destroy(link t);
#endif
binarytree.c
/* binarytree.c */
#include <stdlib.h>
#include "binarytree.h"
static link make_node(unsigned char item)
{
link p = malloc(sizeof *p);
p->item = item;
p->l = p->r = NULL;
return p;
}
static void free_node(link p)
{
free(p);
}
link init(unsigned char VLR[], unsigned char LVR[], int n)
{
link t;
int k;
if (n <= 0)
return NULL;
for (k = 0; VLR[0] != LVR[k]; k++);
t = make_node(VLR[0]);
t->l = init(VLR+1, LVR, k);
t->r = init(VLR+1+k, LVR+1+k, n-k-1);
return t;
}
void pre_order(link t, void (*visit)(link))
{
if (!t)
return;
visit(t);
pre_order(t->l, visit);
pre_order(t->r, visit);
}
void in_order(link t, void (*visit)(link))
{
if (!t)
return;
in_order(t->l, visit);
visit(t);
in_order(t->r, visit);
}
void post_order(link t, void (*visit)(link))
{
if (!t)
return;
post_order(t->l, visit);
post_order(t->r, visit);
visit(t);
}
int count(link t)
{
if (!t)
return 0;
return 1 + count(t->l) + count(t->r);
}
int depth(link t)
{
int dl, dr;
if (!t)
return 0;
dl = depth(t->l);
dr = depth(t->r);
return 1 + (dl > dr ? dl : dr);
}
void destroy(link t)
{
post_order(t, free_node);
}
main.c
/* main.c */
#include <stdio.h>
#include "binarytree.h"
void print_item(link p)
{
printf("%d", p->item);
}
int main()
{
unsigned char pre_seq[] = { 4, 2, 1, 3, 6, 5, 7 };
unsigned char in_seq[] = { 1, 2, 3, 4, 5, 6, 7 };
link root = init(pre_seq, in_seq, 7);
pre_order(root, print_item);
putchar('\n');
in_order(root, print_item);
putchar('\n');
post_order(root, print_item);
putchar('\n');
printf("count=%d depth=%d\n", count(root), depth(root));
destroy(root);
return 0;
}
排序二叉树
排序二叉树(BST,Binary Search Tree)具有这样的性质:对于二叉树中的任意节点,如果它有左子树或右子树, 则该节点的数据成员大于左子树所有节点的数据成员,且小于右子树所有节点的数据成员。
bst.h
/* bst.h */
#ifndef BST_H
#define BST_H
typedef struct node *link;
struct node {
unsigned char item;
link l, r;
};
link search(link t, unsigned char key);
link insert(link t, unsigned char key);
link delete(link t, unsigned char key);
void print_tree(link t);
#endif
bst.c
/* bst.c */
#include <stdlib.h>
#include <stdio.h>
#include "bst.h"
static link make_node(unsigned char item)
{
link p = malloc(sizeof *p);
p->item = item;
p->l = p->r = NULL;
return p;
}
static void free_node(link p)
{
free(p);
}
link search(link t, unsigned char key)
{
if (!t)
return NULL;
if (t->item > key)
return search(t->l, key);
if (t->item < key)
return search(t->r, key);
/* if (t->item == key) */
return t;
}
link insert(link t, unsigned char key)
{
if (!t)
return make_node(key);
if (t->item > key) /* insert to left subtree */
t->l = insert(t->l, key);
else /* if (t->item <= key), insert to right subtree */
t->r = insert(t->r, key);
return t;
}
link delete(link t, unsigned char key)
{
link p;
if (!t)
return NULL;
if (t->item > key) /* delete from left subtree */
t->l = delete(t->l, key);
else if (t->item < key) /* delete from right subtree */
t->r = delete(t->r, key);
else { /* if (t->item == key) */
if (t->l == NULL && t->r == NULL) { /* if t is leaf node */
free_node(t);
t = NULL;
} else if (t->l) { /* if t has left subtree */
/* replace t with the rightmost node in left subtree */
for (p = t->l; p->r; p = p->r);
t->item = p->item;
t->l = delete(t->l, t->item);
} else { /* if t has right subtree */
/* replace t with the leftmost node in right subtree */
for (p = t->r; p->l; p = p->l);
t->item = p->item;
t->r = delete(t->r, t->item);
}
}
return t;
}
void print_tree(link t)
{
if (t) {
printf("(");
printf("%d", t->item);
print_tree(t->l);
print_tree(t->r);
printf(")");
} else
printf("()");
}
main.c
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "bst.h"
#define RANGE 100
#define N 6
void print_item(link p)
{
printf("%d", p->item);
}
int main()
{
int i, key;
link root = NULL;
srand(time(NULL));
for (i = 0; i < N; i++)
root = insert(root, rand() % RANGE);
printf("\t\\tree");
print_tree(root);
printf("\n\n");
while (root) {
key = rand() % RANGE;
if (search(root, key)) {
printf("delete %d in tree\n", key);
root = delete(root, key);
printf("\t\\tree");
print_tree(root);
printf("\n\n");
}
}
}
TCP/iP协议基础
网络七层协议:
层 | 常用设备 | 常用协议 |
---|---|---|
7 应用层 | HTTP、 FTP、 SMTP、 TELNET、 NFS | |
6 表示层 | URL加密、 口令加密、 图片编解码 等 | |
5 会话层 | ||
4 传输层 | 进程、 端口 | TCP、 UDP |
3 网络层 | 路由器、 多层交换机、 防火墙 | IP |
2 数据链路层 | 网卡、 网桥、 二层交换机 等 | ARP、 RARP |
1 物理层 | 集线器、 中继器、 网线 等 | Rj45、 802.3 |
注意,虽然IP、ARP和RARP数据报都需要以太网驱动程序来封装成帧,但是从功能上划分,ARP和RARP属于链路层,IP属于网络层。 虽然ICMP、IGMP、TCP、UDP的数据都需要IP协议来封装成数据报,但是从功能上划分,ICMP、IGMP与IP同属于网络层,TCP和UDP属于传输层。
ARP数据报格式
ARP协议:获得目的主机的硬件地址。
看个例子:
请求帧:
以太网首部(14字节)
0000: ff ff ff ff ff ff 00 05 5d 61 58 a8 08 06
ARP帧(28字节)
0000: 00 01
0010: 08 00 06 04 00 01 00 05 5d 61 58 a8 c0 a8 00 37
0020: 00 00 00 00 00 00 c0 a8 00 02
填充位(18字节)
0020: 00 77 31 d2 50 10
0030: fd 78 41 d3 00 00 00 00 00 00 00 00
以太网首部:目的主机采用广播地址,源主机的MAC地址是00:05:5d:61:58:a8,上层协议类型0x0806表示ARP。 ARP帧:硬件类型0x0001表示以太网,协议类型0x0800表示IP协议,硬件地址(MAC地址)长度为6,协议地址(IP地址)长度为4, op为0x0001表示请求目的主机的MAC地址,源主机MAC地址为00:05:5d:61:58:a8,源主机IP地址为c0 a8 00 37(192.168.0.55), 目的主机MAC地址全0待填写,目的主机IP地址为c0 a8 00 02(192.168.0.2)。 由于以太网规定最小数据长度为46字节,ARP帧长度只有28字节,因此有18字节填充位,填充位的内容没有定义,与具体实现相关。
应答帧:
以太网首部
0000: 00 05 5d 61 58 a8 00 05 5d a1 b8 40 08 06
ARP帧
0000: 00 01
0010: 08 00 06 04 00 02 00 05 5d a1 b8 40 c0 a8 00 02
0020: 00 05 5d 61 58 a8 c0 a8 00 37
填充位
0020: 00 77 31 d2 50 10
0030: fd 78 41 d3 00 00 00 00 00 00 00 00
以太网首部:目的主机的MAC地址是00:05:5d:61:58:a8,源主机的MAC地址是00:05:5d:a1:b8:40,上层协议类型0x0806表示ARP。 ARP帧:硬件类型0x0001表示以太网,协议类型0x0800表示IP协议,硬件地址(MAC地址)长度为6,协议地址(IP地址)长度为4, op为0x0002表示应答,源主机MAC地址为00:05:5d:a1:b8:40,源主机IP地址为c0 a8 00 02(192.168.0.2), 目的主机MAC地址为00:05:5d:61:58:a8,目的主机IP地址为c0 a8 00 37(192.168.0.55)。
汇编程序
一个简单示例:
#PURPOSE: Simple program that exits and returns a
#status code back to the Linux kernel
#
#INPUT:none
#
#OUTPUT: returns a status code. This can be viewed
#by typing
#
#echo $?
#
#after running the program
#
#VARIABLES:
#%eax holds the system call number
#%ebx holds the return status
#
.section .data
.section .text
.globl _start
_start:
movl $1, %eax # this is the linux kernel command
# number (system call) for exiting
# a program
movl $4, %ebx # this is the status number we will
# return to the operating system.
# Change this around and it will
# return different things to
# echo $?
int $0x80 # this wakes up the kernel to run
# the exit command
找到最大值示例:
max.s:
#PURPOSE: This program finds the maximum number of a
#set of data items.
#
#VARIABLES: The registers have the following uses:
#
# %edi - Holds the index of the data item being examined
# %ebx - Largest data item found
# %eax - Current data item
#
# The following memory locations are used:
#
# data_items - contains the item data. A 0 is used
# to terminate the data
#
.section .data
data_items: #These are the data items
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.section .text
.globl _start
_start:
movl $0, %edi
# move 0 into the index register
movl data_items(,%edi,4), %eax # load the first byte of data
movl %eax, %ebx
# since this is the first item, %eax is
# the biggest
start_loop:
# start loop
cmpl $0, %eax
# check to see if we've hit the end
je loop_exit
incl %edi
# load next value
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax
# compare values
jle start_loop
# jump to loop beginning if the new
# one isn't bigger
movl %eax, %ebx
# move the value as the largest
jmp start_loop
# jump to loop beginning
loop_exit:
# %ebx is the status code for the _exit system call
# and it already has the maximum number
movl $1, %eax
#1 is the _exit() syscall
int $0x80
用汇编器(Assembler) as 把汇编程序中的助记符翻译成机器指令:
as max.s -o max.o
用链接器(Linker,或Link Editor) ld 把目标文件 max.o 链接成可执行文件 max:
ld max.o -o max
程序运行:
./max
用特殊变量 $? 得到上一条命令的退出状态:
echo $?
看一下目标文件max.o的ELF Header和Section Header Table:
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: REL (可重定位文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x0
程序头起点: 0 (bytes into file)
Start of section headers: 504 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 0 (字节)
Number of program headers: 0
节头大小: 64 (字节)
节头数量: 8
字符串表索引节头: 7
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
000000000000002d 0000000000000000 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 00000190
0000000000000030 0000000000000018 I 5 1 8
[ 3] .data PROGBITS 0000000000000000 0000006d
0000000000000038 0000000000000000 WA 0 0 1
[ 4] .bss NOBITS 0000000000000000 000000a5
0000000000000000 0000000000000000 WA 0 0 1
[ 5] .symtab SYMTAB 0000000000000000 000000a8
00000000000000c0 0000000000000018 6 7 8
[ 6] .strtab STRTAB 0000000000000000 00000168
0000000000000028 0000000000000000 0 0 1
[ 7] .shstrtab STRTAB 0000000000000000 000001c0
0000000000000031 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
本文件中没有程序头。
重定位节 '.rela.text' 位于偏移量 0x190 含有 2 个条目:
偏移量 信息 类型 符号值 符号名称 + 加数
000000000009 00020000000b R_X86_64_32S 0000000000000000 .data + 0
00000000001a 00020000000b R_X86_64_32S 0000000000000000 .data + 0
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.symtab' contains 8 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 SECTION LOCAL DEFAULT 1
2: 0000000000000000 0 SECTION LOCAL DEFAULT 3
3: 0000000000000000 0 SECTION LOCAL DEFAULT 4
4: 0000000000000000 0 NOTYPE LOCAL DEFAULT 3 data_items
5: 000000000000000f 0 NOTYPE LOCAL DEFAULT 1 start_loop
6: 0000000000000026 0 NOTYPE LOCAL DEFAULT 1 loop_exit
7: 0000000000000000 0 NOTYPE GLOBAL DEFAULT 1 _start
No version information found in this file.
机器指令反汇编(Disassemble)看一下:
objdump -d max.o
max.o: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: bf 00 00 00 00 mov $0x0,%edi
5: 67 8b 04 bd 00 00 00 mov 0x0(,%edi,4),%eax
c: 00
d: 89 c3 mov %eax,%ebx
000000000000000f <start_loop>:
f: 83 f8 00 cmp $0x0,%eax
12: 74 12 je 26 <loop_exit>
14: ff c7 inc %edi
16: 67 8b 04 bd 00 00 00 mov 0x0(,%edi,4),%eax
1d: 00
1e: 39 d8 cmp %ebx,%eax
20: 7e ed jle f <start_loop>
22: 89 c3 mov %eax,%ebx
24: eb e9 jmp f <start_loop>
0000000000000026 <loop_exit>:
26: b8 01 00 00 00 mov $0x1,%eax
2b: cd 80 int $0x80
看一下可执行文件max:
readelf -a max
ELF 头:
Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
类别: ELF64
数据: 2 补码,小端序 (little endian)
版本: 1 (current)
OS/ABI: UNIX - System V
ABI 版本: 0
类型: EXEC (可执行文件)
系统架构: Advanced Micro Devices X86-64
版本: 0x1
入口点地址: 0x4000b0
程序头起点: 64 (bytes into file)
Start of section headers: 656 (bytes into file)
标志: 0x0
本头的大小: 64 (字节)
程序头大小: 56 (字节)
Number of program headers: 2
节头大小: 64 (字节)
节头数量: 6
字符串表索引节头: 5
节头:
[号] 名称 类型 地址 偏移量
大小 全体大小 旗标 链接 信息 对齐
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 00000000004000b0 000000b0
000000000000002d 0000000000000000 AX 0 0 1
[ 2] .data PROGBITS 00000000006000dd 000000dd
0000000000000038 0000000000000000 WA 0 0 1
[ 3] .symtab SYMTAB 0000000000000000 00000118
0000000000000108 0000000000000018 4 7 8
[ 4] .strtab STRTAB 0000000000000000 00000220
0000000000000043 0000000000000000 0 0 1
[ 5] .shstrtab STRTAB 0000000000000000 00000263
0000000000000027 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
There are no section groups in this file.
程序头:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000000dd 0x00000000000000dd R E 0x200000
LOAD 0x00000000000000dd 0x00000000006000dd 0x00000000006000dd
0x0000000000000038 0x0000000000000038 RW 0x200000
Section to Segment mapping:
段节...
00 .text
01 .data
There is no dynamic section in this file.
该文件中没有重定位信息。
The decoding of unwind sections for machine type Advanced Micro Devices X86-64 is not currently supported.
Symbol table '.symtab' contains 11 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000004000b0 0 SECTION LOCAL DEFAULT 1
2: 00000000006000dd 0 SECTION LOCAL DEFAULT 2
3: 0000000000000000 0 FILE LOCAL DEFAULT ABS test232.o
4: 00000000006000dd 0 NOTYPE LOCAL DEFAULT 2 data_items
5: 00000000004000bf 0 NOTYPE LOCAL DEFAULT 1 start_loop
6: 00000000004000d6 0 NOTYPE LOCAL DEFAULT 1 loop_exit
7: 00000000004000b0 0 NOTYPE GLOBAL DEFAULT 1 _start
8: 0000000000600115 0 NOTYPE GLOBAL DEFAULT 2 __bss_start
9: 0000000000600115 0 NOTYPE GLOBAL DEFAULT 2 _edata
10: 0000000000600118 0 NOTYPE GLOBAL DEFAULT 2 _end
No version information found in this file.
参考资料
Linux C编程一站式学习 http://akaedu.github.io/book/
Linux基本操作学习 http://happypeter.github.io/LGCB/book/index.html
你是如何学习 Linux 编程的? https://www.zhihu.com/question/20730157?sort=created
十个最值得阅读学习的C开源项目代码 https://blog.csdn.net/deeplee021/article/details/40583877
源码阅读——十个C开源项目 https://my.oschina.net/zhoukuo/blog/335788
源码开放学ARM http://www.lumit.org/