如何使用 Linux 的 ar 命令创建静态库

在开发软件时,使用 Linux 的 ar
命令创建函数库。本教程将向您展示如何创建静态库、修改它以及在程序中使用它,并提供示例代码。
ar
命令是一个真正的老手——它从 1971 年就出现了。名称 ar
引用了该工具最初的预期用途,即创建存档文件。存档文件是充当其他文件容器的单个文件。有时对于许多其他文件。可以将文件添加到存档中、从中删除或从存档中提取文件。寻找此类功能的人不再求助于 ar
。该角色已被其他实用程序接管,例如 tar
。
不过,ar
命令仍用于一些特殊用途。 ar
用于创建静态库。这些用于软件开发。 ar
也用于创建包文件,例如 Debian Linux 发行版及其衍生产品(如 Ubuntu)中使用的“.deb”文件。
我们将完成创建和修改静态库所需的步骤,并演示如何在程序中使用该库。为此,我们需要满足静态库的要求。这个库的目的是编码文本字符串和解码编码文本。
请注意,这是一个用于演示目的的快速而肮脏的 hack。不要将此加密用于任何有价值的东西。它是世界上最简单的代换密码,其中 A 变成 B,B 变成 C,等等。
cipher_encode() 和 cipher_decode() 函数
我们将在名为“library”的目录中工作,稍后我们将创建一个名为“test”的子目录。
我们在这个目录中有两个文件。在名为 cipher_encode.c 的文本文件中,我们有 cipher_encode()
函数:
void cipher_encode(char *text)
{
for (int i=0; text[i] != 0x0; i++) {
text[i]++;
}
} // end of cipher_encode
相应的 cipher_decode()
函数位于名为 cipher_decode.c 的文本文件中:
void cipher_decode(char *text)
{
for (int i=0; text[i] != 0x0; i++) {
text[i]--;
}
} // end of cipher_decode
包含编程指令的文件称为源代码文件。我们将创建一个名为 libcipher.a 的库文件。它将包含这两个源代码文件的编译版本。我们还将创建一个名为 libcipher.h 的短文本文件。这是一个头文件,其中包含我们新库中两个函数的定义。
拥有库和头文件的任何人都可以在自己的程序中使用这两个函数。他们不需要重新发明轮子和重新编写功能;他们只是利用我们图书馆中的副本。
编译 cipher_encode.c 和 cipher_decode.c 文件
要编译源代码文件,我们将使用标准 GNU 编译器 gcc
。 -c
(编译,无链接)选项告诉 gcc
编译文件然后停止。它从每个源代码文件生成一个称为目标文件的中间文件。 gcc
链接器通常获取所有目标文件并将它们链接在一起以生成可执行程序。我们使用 -c
选项跳过了这一步。我们只需要目标文件。
让我们检查一下我们是否拥有我们认为拥有的文件。
ls -l

这两个源代码文件存在于此目录中。让我们使用 gcc
将它们编译成目标文件。
gcc -c cipher_encode.c
gcc -c cipher_decode.c
如果一切顺利,gcc
应该没有输出。

这会生成两个与源代码文件同名的目标文件,但扩展名为“.o”。这些是我们需要添加到库文件中的文件。
ls -l

创建 libcipher.a 库
要创建库文件——实际上是一个存档文件——我们将使用 ar
。
我们使用 -c
(创建)选项创建库文件,使用 -r
(添加替换)选项将文件添加到库文件,以及-s
(索引)选项,用于在库文件中创建文件索引。
我们将调用库文件 libcipher.a。我们在命令行上提供该名称,以及我们要添加到库中的目标文件的名称。
ar -crs libcipher.a cipher_encode.o cipher_decode.o

如果我们列出目录中的文件,我们将看到我们现在有一个 libcipher.a 文件。
ls -l

如果我们将 -t
(表)选项与 ar
一起使用,我们可以看到库文件中的模块。
ar -t libcipher.a

创建 libcipher.h 头文件
libcipher.h 文件将包含在任何使用 libcipher.a 库的程序中。 libcipher.h 文件必须包含库中函数的定义。
要创建头文件,我们必须将函数定义键入文本编辑器(如 gedit)中。将文件命名为“libcipher.h”并将其保存在与 libcipher.a 文件相同的目录中。
void cipher_encode(char *text);
void cipher_decode(char *text);
使用 libcipher 库
测试我们的新库的唯一可靠方法是编写一个小程序来使用它。首先,我们将创建一个名为 test 的目录。
mkdir test
我们会将库和头文件复制到新目录中。
cp libcipher.* ./test
我们将切换到新目录。
cd test
让我们检查一下我们的两个文件是否在这里。
ls -l

我们需要创建一个可以使用该库并证明其功能符合预期的小程序。在编辑器中键入以下文本行。将编辑器的内容保存到 test 目录中名为“test.c”的文件中。
#include <stdio.h>
#include <stdlib.h>
#include "libcipher.h"
int main(int argc, char *argv[])
{
char text[]="How-To Geek loves Linux";
puts(text);
cipher_encode(text);
puts(text);
cipher_decode(text);
puts(text);
exit (0);
} // end of main
程序流程非常简单:
- 它包含 libcipher.h 文件,以便它可以查看库函数定义。
- 它创建了一个名为“text”的字符串,并在其中存储了“How-To Geek loves Linux”这个词。
- 它将那个字符串打印到屏幕上。
- 它调用
cipher_encode()
函数对字符串进行编码,并将编码后的字符串打印到屏幕上。 - 调用
cipher_decode()
对字符串进行解码,并将解码后的字符串打印到屏幕上。
要生成test
程序,我们需要编译test.c 程序并链接到库中。 -o
(输出)选项告诉 gcc
调用它生成的可执行程序的内容。
gcc test.c libcipher.a -o test

如果 gcc
自动将您返回到命令提示符,则一切正常。现在让我们测试我们的程序。关键时刻:
./test

我们看到了预期的输出。 test
程序打印纯文本打印加密文本,然后打印解密文本。它正在使用我们新库中的函数。我们的图书馆正在运作。

成功。但为什么要停在那里呢?
向库中添加另一个模块
让我们向库中添加另一个函数。我们将添加一个函数,程序员可以使用该函数来显示他们正在使用的库的版本。我们需要创建新函数,编译它,并将新的目标文件添加到现有的库文件中。
在编辑器中键入以下行。将编辑器的内容保存到 library 目录中名为 cipher_version.c 的文件中。
#include <stdio.h>
void cipher_version(void)
{
puts("How-To Geek :: VERY INSECURE Cipher Library");
puts("Version 0.0.1 Alpha\n");
} // end of cipher_version
我们需要将新函数的定义添加到 libcipher.h 头文件中。在该文件的底部添加一个新行,使其看起来像这样:
void cipher_encode(char *text);
void cipher_decode(char *text);
void cipher_version(void);
保存修改后的 libcipher.h 文件。
我们需要编译 cipher_version.c 文件,以便我们有一个 cipher_version.o 目标文件。
gcc -c cipher_version.c

这将创建一个 cipher_version.o 文件。我们可以使用以下命令将新目标文件添加到 libcipher.a 库中。 -v
(详细)选项让通常无声的 ar
告诉我们它做了什么。
ar -rsv libcipher.a cipher_version.o

新目标文件被添加到库文件中。 ar
打印确认信息。 “a”的意思是“添加”。

我们可以使用 -t
(table) 选项来查看库文件中有哪些模块。
ar -t libcipher.a

我们的库文件中现在有三个模块。让我们使用新功能。
使用 cipher_version() 函数。
让我们从测试目录中删除旧的库和头文件,复制新文件,然后改回测试目录。
我们将删除旧版本的文件。
rm ./test/libcipher.*
我们会将新版本复制到测试目录中。
cp libcipher.* ./test
我们将更改为测试目录。
cd test

现在我们可以修改 test.c 程序,使其使用新的库函数。
我们需要在调用 cipher_version()
函数的 test.c 程序中添加一行。我们将把它放在第一个 puts(text);
行之前。
#include <stdio.h>
#include <stdlib.h>
#include "libcipher.h"
int main(int argc, char *argv[])
{
char text[]="How-To Geek loves Linux";
// new line added here
cipher_version();
puts(text);
cipher_encode(text);
puts(text);
cipher_decode(text);
puts(text);
exit (0);
} // end of main
将其保存为 test.c。我们现在可以编译它并测试新函数是否可以运行。
gcc test.c libcipher.a -o test

让我们运行新版本的 test
:

新功能正在运行。我们可以在 test
输出的开头看到库的版本。
但是可能会有问题。
替换库中的模块
这不是该库的第一个版本;这是第二个。我们的版本号不正确。第一个版本中没有 cipher_version()
函数。这个可以。所以这应该是版本“0.0.2”。我们需要用更正的函数替换库中的 cipher_version()
函数。
值得庆幸的是,ar
使这件事变得非常容易。
首先,让我们编辑 library 目录中的 cipher_version.c 文件。将“版本 0.0.1 Alpha”文本更改为“版本 0.0.2 Alpha”。它应该是这样的:
#include <stdio.h>
void cipher_version(void)
{
puts("How-To Geek :: VERY INSECURE Cipher Library");
puts("Version 0.0.2 Alpha\n");
} // end of cipher_version
保存此文件。我们需要再次编译它以创建一个新的 cipher_version.o 目标文件。
gcc -c cipher_version.c

现在,我们将替换库中现有的 cipher_version.o 对象为我们新编译的版本。
我们之前使用 -r
(添加替换)选项将新模块添加到库中。当我们将它与库中已存在的模块一起使用时,ar
会将旧版本替换为新版本。 -s
(索引)选项将更新库索引,-v
(详细)选项将使 ar
告诉我们它做了什么.
ar -rsv libcipher.a cipher_version.o

这次 ar
报告它已经替换了 cipher_version.o 模块。 “r”表示已替换。

使用更新的 cipher_version() 函数
我们应该使用修改后的库并检查它是否有效。
我们将库文件复制到测试目录。
cp libcipher.* ./test
我们将更改为测试目录。
cd ./test
我们需要用我们的新库再次编译我们的测试程序。
gcc test.c libcipher.a -o test
现在我们可以测试我们的程序了。
./test

测试程序的输出是我们所期望的。版本字符串中显示了正确的版本号,并且加密和解密例程正在运行。
从库中删除模块
毕竟这似乎很遗憾,但让我们从库文件中删除 cipher_version.o 文件。
为此,我们将使用 -d
(删除)选项。我们还将使用 -v
(详细)选项,以便 ar
告诉我们它做了什么。我们还将包含 -s
(索引)选项以更新库文件中的索引。
ar -dsv libcipher.a cipher_version.o

ar
报告它已删除该模块。 “d”表示“已删除”。
如果我们要求 ar
列出库文件中的模块,我们会看到我们回到了两个模块。
ar -t libcipher.a

如果您要从库中删除模块,请记住从库头文件中删除它们的定义。
分享你的代码
库使代码以一种实用但私密的方式共享。您提供库文件和头文件的任何人都可以使用您的库,但您的实际源代码仍然是私有的。
Linux Commands | ||
Files | tar · pv · cat · tac · chmod · grep · diff · sed · ar · man · pushd · popd · fsck · testdisk · seq · fd · pandoc · cd · $PATH · awk · join · jq · fold · uniq · journalctl · tail · stat · ls · fstab · echo · less · chgrp · chown · rev · look · strings · type · rename · zip · unzip · mount · umount · install · fdisk · mkfs · rm · rmdir · rsync · df · gpg · vi · nano · mkdir · du · ln · patch · convert · rclone · shred · srm · scp · gzip · chattr · cut · find · umask · wc | |
Processes | alias · screen · top · nice · renice · progress · strace · systemd · tmux · chsh · history · at · batch · free · which · dmesg · chfn · usermod · ps · chroot · xargs · tty · pinky · lsof · vmstat · timeout · wall · yes · kill · sleep · sudo · su · time · groupadd · usermod · groups · lshw · shutdown · reboot · halt · poweroff · passwd · lscpu · crontab · date · bg · fg · pidof · nohup · pmap | |
Networking | netstat · ping · traceroute · ip · ss · whois · fail2ban · bmon · dig · finger · nmap · ftp · curl · wget · who · whoami · w · iptables · ssh-keygen · ufw · arping · firewalld |
RELATED: Best Linux Laptops for Developers and Enthusiasts