【程序员的自我修养03】深入了解ELF文件格式

绪论

        大家好,欢迎来到【程序员的自我修养】专栏。正如其专栏名,本专栏主要分享学习《程序员的自我修养——链接、装载与库》的知识点以及结合自己的工作经验以及思考。编译原理相关知识本身就比较有难度,我会尽自己最大的努力,争取深入浅出。若你希望与一群志同道合的朋友一起学习,也希望加入到我们的学习群中。文末有加入方式。

简介

        从上文【程序员的自我修养02】初识ELF文件格式-CSDN博客中,我们已经初步了解ELF文件布局,其中应该有文件头、代码段、数据段、.bss段、段表等。本文我们从段表开始,进一步了解ELF文件的布局。请耐心看完,我也会分享一些不常用,但是很有趣的小技巧。

.section tables

我们可通过readelf -S example.o命令查看段表信息。如下:

yihua@ubuntu:~/test/example$ readelf -S example.o
There are 13 section headers, starting at offset 0x450:

Section Headers:
  [Nr] Name              Type             Address           Offset   Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000 0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000000000  00000040 0000000000000059  0000000000000000  AX       0     0     1
  [ 2] .rela.text        RELA             0000000000000000  00000340 0000000000000078  0000000000000018   I      10     1     8
  [ 3] .data             PROGBITS         0000000000000000  0000009c 0000000000000008  0000000000000000  WA       0     0     4
  [ 4] .bss              NOBITS           0000000000000000  000000a4 0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  000000a4 0000000000000004  0000000000000000   A       0     0     1
  [ 6] .comment          PROGBITS         0000000000000000  000000a8 000000000000002a  0000000000000001  MS       0     0     1
  [ 7] .note.GNU-stack   PROGBITS         0000000000000000  000000d2 0000000000000000  0000000000000000           0     0     1
  [ 8] .eh_frame         PROGBITS         0000000000000000  000000d8 0000000000000058  0000000000000000   A       0     0     8
  [ 9] .rela.eh_frame    RELA             0000000000000000  000003b8 0000000000000030  0000000000000018   I      10     8     8
  [10] .symtab           SYMTAB           0000000000000000  00000130 0000000000000198  0000000000000018          11    11     8
  [11] .strtab           STRTAB           0000000000000000  000002c8 0000000000000075  0000000000000000           0     0     1
  [12] .shstrtab         STRTAB           0000000000000000  000003e8 0000000000000061  0000000000000000           0     0     1

 

由上分析:

        由第一句可知,本段表共有13个段,并在文件中offset 0x450 处开始,这与上一章中文件头内容是匹配上的。(0x450是1104的的16进制)。

我们先看一下表的列项:

  • Name :表示段名称。可知ELF文件中不仅仅包含.text、.data、.bss,还有其它内容。
  • Type : 表示段的类型。因为链接器并不是通过段名称确定段类型的。比如.text段就一定是代码段吗?常见的段类型有:

        PROGBITS:程序段。代码段和数据段都是这种类型。

        SYMTAB:符号表内容。也就是我们nm命令查看的内容。

        STRTAB:表示该段的内容为字符串表

        RELA:重定位表。该段包含了重定位信息。在下个章节静态链接阶段我们会再讨论。

        HASH:符号的哈希表。

        DYNAMIC:动态链接信息。

        NOBITS:表示该段没有内容比如.bss段。如上所示,.bss和.rodata段的偏移是一样的。

        REL:该段包含重定位信息。在下个章节静态链接阶段我们会再讨论

        DNYSYM:动态链接的符号表。

  • Address: 段虚拟地址。即程序加载时,该段要加载到虚拟内存的地址。这是一个很关键的知识点:代码的虚拟地址在编译阶段就已经确定了,并不是运行时由系统决定的。gdb调试时,定位动态库的调试信息,就是依赖于该点。

    但是为什么上述为NULL呢?

    因为example.o是可重定位文件,虚拟机地址还未确认,只有可执行文件各端的虚拟地址是确认的,如下:

yihua@ubuntu:~/test/example$ readelf -S example
There are 29 section headers, starting at offset 0x19f8:
Section Headers:
  [Nr] Name              Type             Address           Offset    Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000  0000000000000000  0000000000000000           0     0     0
  [ 1] .interp           PROGBITS         0000000000000238  00000238  000000000000001c  0000000000000000   A       0     0     1
  [ 2] .note.ABI-tag     NOTE             0000000000000254  00000254  0000000000000020  0000000000000000   A       0     0     4
  [ 3] .note.gnu.build-i NOTE             0000000000000274  00000274  0000000000000024  0000000000000000   A       0     0     4
  [ 4] .gnu.hash         GNU_HASH         0000000000000298  00000298  000000000000001c  0000000000000000   A       5     0     8
  [ 5] .dynsym           DYNSYM           00000000000002b8  000002b8  00000000000000a8  0000000000000018   A       6     1     8
  [ 6] .dynstr           STRTAB           0000000000000360  00000360  0000000000000084  0000000000000000   A       0     0     1
  [ 7] .gnu.version      VERSYM           00000000000003e4  000003e4  000000000000000e  0000000000000002   A       5     0     2
  [ 8] .gnu.version_r    VERNEED          00000000000003f8  000003f8  0000000000000020  0000000000000000   A       6     1     8

 

  • Offset: 该段位于文件中的偏移注:.bss 段的该参数就没有意义,因为它并不存在于文件中
  • Size:该段内容长度。
  • EntSize: 项的长度。有些段包含了一些大小固定的项,比如符号表,它包含的每个符号所占的大小都是一样的。对于这种段,EntSize 表示每个项的大小。如果为0,则表示该段不包含固定大小的项。
  • Flags:段的标志位。该标志位决定该段在进程虚拟地址的属性。我们一般只要关注:

【A】表示该段在进程空间中必须分配空间。比如代码段,数据段,.bss段一定会有该标识。但是符号表,调试信息则不需要。

【W】表示该段在进程空间中可写。比如.bss 和数据段。而代码段是不可写的。

【X】表示该段在进程空间中可被执行。一般指代码段。

  • Link 和 Info:表示段的链接信息。
  • Align:表示该段对地址对其的要求。

        通过对段表的了解,我们现在对example.o的内容布局更详细了,如下:

        其中空白内容,则是因为各段有字节对齐要求,进行了对齐。而其它部分的内容暂不关注,一般就是调试信息等。

        综上所述,我们基本已经了解了ELF文件格式的布局,接下来,我们尝试深入了解各个段的内容。

.text

        我们可以通过objdump -s -d example.o查看目标文件的代码段。其中-s表示将所有段的内容以十六进制输出,-d表示将所有包含指令的段反汇编。输出如下:

yihua@ubuntu:~/test/example$ objdump -s -d example.o
example.o:     file format elf64-x86-64
Contents of section .text:
 0000 554889e5 4883ec10 897dfc8b 45fc89c6  UH..H....}..E...
 0010 488d3d00 000000b8 00000000 e8000000  H.=.............
 0020 0090c9c3 554889e5 4883ec10 c745f801  ....UH..H....E..
 0030 0000008b 15000000 008b0500 00000001  ................
 0040 c28b45f8 01c28b45 fc01d089 c7e80000  ..E....E........
 0050 0000b800 000000c9 c3                 .........
Contents of section .data:
 0000 54000000 55000000                    T...U...
Contents of section .rodata:
 0000 25640a00                             %d..
Contents of section .comment:
 0000 00474343 3a202855 62756e74 7520372e  .GCC: (Ubuntu 7.
 0010 352e302d 33756275 6e747531 7e31382e  5.0-3ubuntu1~18.
 0020 30342920 372e352e 3000               04) 7.5.0.
Contents of section .eh_frame:
 0000 14000000 00000000 017a5200 01781001  .........zR..x..
 0010 1b0c0708 90010000 1c000000 1c000000  ................
 0020 00000000 24000000 00410e10 8602430d  ....$....A....C.
 0030 065f0c07 08000000 1c000000 3c000000  ._..........<...
 0040 00000000 35000000 00410e10 8602430d  ....5....A....C.
 0050 06700c07 08000000                    .p......

Disassembly of section .text:

0000000000000000 <func1>:
   0:   55                      push   %rbp
   1:   48 89 e5                mov    %rsp,%rbp
   4:   48 83 ec 10             sub    $0x10,%rsp
   8:   89 7d fc                mov    %edi,-0x4(%rbp)
   b:   8b 45 fc                mov    -0x4(%rbp),%eax
   e:   89 c6                   mov    %eax,%esi
  10:   48 8d 3d 00 00 00 00    lea    0x0(%rip),%rdi        # 17 <func1+0x17>
  17:   b8 00 00 00 00          mov    $0x0,%eax
  1c:   e8 00 00 00 00          callq  21 <func1+0x21>
  21:   90                      nop
  22:   c9                      leaveq
  23:   c3                      retq

0000000000000024 <main>:
  24:   55                      push   %rbp
  25:   48 89 e5                mov    %rsp,%rbp
  28:   48 83 ec 10             sub    $0x10,%rsp
  2c:   c7 45 f8 01 00 00 00    movl   $0x1,-0x8(%rbp)
  33:   8b 15 00 00 00 00       mov    0x0(%rip),%edx        # 39 <main+0x15>
  39:   8b 05 00 00 00 00       mov    0x0(%rip),%eax        # 3f <main+0x1b>
  3f:   01 c2                   add    %eax,%edx
  41:   8b 45 f8                mov    -0x8(%rbp),%eax
  44:   01 c2                   add    %eax,%edx
  46:   8b 45 fc                mov    -0x4(%rbp),%eax
  49:   01 d0                   add    %edx,%eax
  4b:   89 c7                   mov    %eax,%edi
  4d:   e8 00 00 00 00          callq  52 <main+0x2e>
  52:   b8 00 00 00 00          mov    $0x0,%eax
  57:   c9                      leaveq
  58:   c3                      retq

如上所示:

        绿色字体:用16进制打印了.text、.data、.rodata、.comment、.eh_frame段的信息,其长度与段表中显示的长度是一一对应的。

        黄色字体:则是代码段经过反汇编的内容。

        正常情况下,我们一般是不需要关注代码的汇编层面的。但是当遇到一些棘手问题时,特别是踩内存导致得到异常情况,我们就有可能需要分析程序的汇编语句了。我曾经遇到的一个案例。大致问题原因如下:

        客户的提供的第三方代码出现了踩内存问题,修改了上层栈内容。导致调用客户接口之后,在回到我的接口后,执行打印语句,类似:printf("hello world\n");就会crash。这个问题困扰了客户,和同事很长时间。

我的排查思路,大致如下:

  1. 通过gdb 查看crash 堆栈信息,发现入参hello world是一个非法地址。
  2. 通过反汇编,查看相应代码段的入参保存在哪个寄存器,以及这个寄存器由哪里赋值而来。
  3. 最终再结合gdb watch指令,一点点逆向排查,确定了是客户提供的sdk 操作了 其它函数栈里面的内容(越界)。

注:"有时候"我们通过虚拟机里面的objdump 指令,并不能反汇编目标平台的程序。那是因为代码段是二进制代码,不同平台是不一样的。因而非x86平台的程序,虚拟机是无法解析的。需要使用交叉编译工具链中对应的调试工具

.data & .rodata

        我们知道.data 段保存的是那些已经初始化了的全局变量和局部静态变量.rodata 段保存的是只读数据,一般是程序里面的只读变量(const 修饰的变量)和字符串常量。但是真是如此吗?我们不妨进一步验证以下。

        我们由段表可知:.data段的起始地址是0x9c,且长度 8字节; .rodata 段的起始地址0xa4,且长度是4。我们尝试用二进制文本编辑器查看example.o文件。如下:

        结合上篇文章中描述的该可重定位文件是小端字节序。因此这两个值就分别对应0x54和0x55。转为十进制,就正好对应int global_init_var = 84;static int static_var = 85;。是不是很惊喜~~

.rodata段的内容如下:

        可知.rodata起始保存的就是我们printf接口中的字符串常量"%d\n",字符串默认结尾有\0。

tips:

        我既然我们已经知道数据在example.o的具体位置,如果我们通过二进制编辑器修改这个值,是否能达到修改最终的输出结果呢?于是尝试如下:

        最终结果如我们所想,的确可以通过该方式简单修改程序的逻辑。但是在实际工作场景中,如何应用起来,我还不清楚。暂且当作一个小技巧吧。

.bss

        .bss段用于存放为未初始化的全局变量和局部静态变量。如演示代码所示,int global_unint_var;static int static_var2;两变量应该保存在该段中。根据段表信息

Section Headers:
  [Nr] Name              Type             Address           Offset   Size              EntSize          Flags  Link  Info  Align
  [ 4] .bss              NOBITS           0000000000000000  000000a4 0000000000000004  0000000000000000  WA       0     0     4
  [ 5] .rodata           PROGBITS         0000000000000000  000000a4 0000000000000004  0000000000000000   A       0     0     1

我有两个疑问:

一、 为什么global_unint_var 和 static_var2两个变量应该占用8个字节,但是.bss 段的Size 只有4Byte?

二、为什么.bss 段和 .rodata段的起始地址都一样?也就是.bss 为什么不存在于文件?

        第一个问题,先不做回答,后面.symtab 小节再讨论。对于问题二,我的理解如下:

        我们知道,未初始化的全局变量和局部静态变量的值默认初始化为0,那么即没有必要在ELF文件中体现。只需要告诉操作系统,bss 段占用4字节,当程序加载时,自然会在虚拟空间中分配对应内存给bss。这样就达到节省磁盘的目的。(注:没有节省内存),那么这4字节表示哪个变量呢?请继续往下看。

.rel.text

        .rel.text 段的类型是REL,也就是说它是一个重定位表。

        我们知道链接器在处理目标文件时,需要对目标文件中某些部位进行重定位,即代码段和数据段中哪些绝对地址的引用需要修改。.ral.text 则是对.text段的重定位表;.rel.data 则是对.data段的重定位表。

        我们可以通过readelf -r example.o查看文件的重定位表,内容如下:

yihua@ubuntu:~/test/example$ readelf -r example.o

Relocation section '.rela.text' at offset 0x340 contains 5 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000013  000500000002 R_X86_64_PC32     0000000000000000 .rodata - 4
00000000001d  000f00000004 R_X86_64_PLT32    0000000000000000 printf - 4
000000000035  000300000002 R_X86_64_PC32     0000000000000000 .data + 0
00000000003b  000400000002 R_X86_64_PC32     0000000000000000 .bss - 4
00000000004e  000d00000002 R_X86_64_PC32     0000000000000000 func1 - 4

Relocation section '.rela.eh_frame' at offset 0x3b8 contains 2 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
000000000020  000200000002 R_X86_64_PC32     0000000000000000 .text + 0
000000000040  000200000002 R_X86_64_PC32     0000000000000000 .text + 24
 

        关于如何理解这些信息,我们再后续静态链接章节描述。大家可以先知道有这个东西,留个印象。

.symtab

        我们知道不同文件间对函数、变量之间的引用,其实就是对符号的引用。链接的过程,其实质就是通过符号将不同的目标文件相互“粘”到一起。因此符号对于一个目标文件就显得是什么重要。

        我们可以通过readelf -s example.o查看文件的符号表。如下:

yihua@ubuntu:~/test/example$ readelf -s example.o

Symbol table '.symtab' contains 17 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS example.c
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3
     4: 0000000000000000     0 SECTION LOCAL  DEFAULT    4
     5: 0000000000000000     0 SECTION LOCAL  DEFAULT    5
     6: 0000000000000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.1801
     7: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.1802
     8: 0000000000000000     0 SECTION LOCAL  DEFAULT    7
     9: 0000000000000000     0 SECTION LOCAL  DEFAULT    8
    10: 0000000000000000     0 SECTION LOCAL  DEFAULT    6
    11: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var
    12: 0000000000000004     4 OBJECT  GLOBAL DEFAULT  COM global_unint_var
    13: 0000000000000000    36 FUNC    GLOBAL DEFAULT    1 func1
    14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND _GLOBAL_OFFSET_TABLE_
    15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
    16: 0000000000000024    53 FUNC    GLOBAL DEFAULT    1 main

  • Value:符号相对应的值。这个值的含义与符号相关,不同的符号表示的含义不同。规则如下:
  • 在目标文件中,如果是符号的定义,并且不是“COM”类型,表示该符号在段中的偏移。比如static_var2.1802符号,则在.bss段的起始地址。同时也回到了上面提到的问题:.bss段保存的是哪一个变量
  • 在目标文件中,如果符号是“COM”类型,则表示对其的类型。比如:global_unint_var符号,要求4字节对其。
  • 在可执行文件中,则表示进程空间的虚拟地址
  • Size:符号大小。
  • Type:符号类型。【NOTYPE】表示该符号是未知的类型、【OBJECT】表示该符号是个数据类型、【SECTION】表示该符号是一个段、【FUNC】表示该符号是函数、【FILE】表示该符号是文件名。
  • Bind:符号绑定信息。【LOCAL】局部符号,对于目标文件外不可见。【GLOBAL】全局符号,对外部可见。【WEAK】弱引用。可参考C语言中弱符号与弱引用的实际应用_c接口声明位弱符号_谢艺华的博客-CSDN博客。可通过该标识,判断客户提供的SDK,是否对外提供了对应接口。
  • Ndx:符号所在的段。如果符号在本目标文件中,那么数字则表示所在的段处于段表中的下标。结合readelf -S 查看。比如3 static_var.1801 则表示static_var.1801符号处于.data段。其也有特殊值:
  • ABS:表示该符号包含了一个绝对值。比如文件名的符号就属于该类型。
  • COM:一般情况下,为初始化的全局变量就是这个类型。比如:global_unint_var
  • UND:未定义的符号。表示该符号在本目标文件被引用到,但是定义在其它目标文件中。

        对于符号我们经常遇到的问题就是链接阶段符号未定义错误。undefined reference to `***。一般去找对应的源码件,查看是否编译或链接库即可。若即编译也链接了,大概率就是符号修饰问题了,可参考该文章深思:C与C++相互调用问题-CSDN博客

其它

        还有其它的段比如:dynamic、.symtab、.plt、.got、.init、.fini。我们在本文就不再详细介绍了,在后续的章节再逐一介绍。还请朋友们关注,不迷路哦~~~。

总结

        本文在上一篇的基础上,进一步了解了ELF的文件格式,了解其文件布局。

        之后再介绍常见的段,代码段,数据段,bss段,符号表等内容,以及工作中可以借鉴的地方。也初步了解到了重定位表的存在及含义,为后面的静态链接做好了铺垫。

        有任何相关问题欢迎留言讨论,我会尽快回复。

        若您正遇到相关问题,苦于没有一群志同道合的朋友交流,探讨。也欢迎加入我们的讨论组群。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/204515.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言期末不挂科——指针进阶篇】【上】

C语言进阶篇【上】 文章目录 C语言进阶篇【上】字符指针数组指针数组传参和指针传参  数组传参  一级指针传参  二级指针传参 前言&#xff1a; 我们在指针初阶篇学习了&#xff1a; 1、指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识一块空间。 2、指…

uniapp在H5端实现PDF和视频的上传、预览、下载

上传 上传页面 <u-form-item :label"(form.ququ3 1 ? 参培 : form.ququ3 2 ? 授课 : ) 证明材料" prop"ququ6" required><u-button click"upload" slot"right" type"primary" icon"arrow-upward" t…

大语言模型新升级:亚马逊云科技2023芯片创新日

在这个充满活力的2023年芯片创新日&#xff0c;Amazon EC2 的副总裁 Dave Brown 与观众分享了他与 EC2 的15年漫长旅程。他的眼中闪烁着对技术的热情&#xff0c;他描述了自己如何与一个才华横溢的团队合作&#xff0c;在这大语言模型与生成式AI的元年中致力于为客户提供最佳的…

Linux:进程状态

目录 1.Linux内核关于进程状态的源代码 2. 运行状态 3. 阻塞状态 4. 挂起 5.Linux中的进程状态 5.1 睡眠状态 5.2 暂停状态 5.3 僵尸进程与孤儿进程 我们在学习进程状态时&#xff0c;老师只是简单的让我们记住下面这张图 1.教材中进程操作系统的进程状态 那么这些…

四、shell - 字符串

目录 1、单引号 2、双引号 3、拼接字符串 3.1 使用双引号拼接 3.2 使用单引号拼接 4、获取字符串长度 ​​​​​​​5、提取子字符串 ​​​​​​​6、查找子字符串 ​​​​​​​字符串是shell编程中最常用最有用的数据类型&#xff08;除了数字和字符串&#xff0…

大模型的RPA应用 | 代理流程自动化(APA),开启智能自动化新纪元

随着技术创新的持续推进&#xff0c;自动化技术已经变得至关重要&#xff0c;成为驱动企业和社会向前发展的核心动力。在自动化的里程碑中&#xff0c;机器人流程自动化&#xff08;RPA&#xff09;已经有效地将简单、重复且规则性的任务自动化。可是随着对处理更为复杂、多变且…

“最甜港姐”走的与众不同之路

网络图片 媒体最新报道&#xff0c;她被誉为“最甜港姐”&#xff0c;曾是TVB炙手可热的当红花旦&#xff0c;却在最当红的时候选择急流勇退。 她不是退圈去相夫教子&#xff0c;而是读书深造&#xff0c;成为一名专业律师。 前不久&#xff0c;又有消息传出&#xff0c;明年…

VUE2+THREE.JS辉光设定和解决辉光导致背景变暗的问题

THREE.JS辉光设定和解决辉光导致背景变暗的问题 THREE.JS 辉光设定THREE.JS 辉光导致背景变暗的问题1.设定背景图片2.初始化辉光3. animate 一直渲染辉光 THREE.JS 辉光设定 给我的设计好的fbx模型,已经设定好了模型发光材质,所以直接添加辉光效果,就可以自动发光 blender模型生…

Docker 安装部署 Sentinel Dashboard

1、下载 jar 包 官方 jar 包下载地址&#xff1a;https://github.com/alibaba/Sentinel/releases 或者点击 链接 直接跳转到下载页 进入链接下载你需要的版本 下载完毕&#xff08;我这里统一放在一个sentinel目录内&#xff09; 2、编写 Dockerfile 文件&#xff08;这里我不…

求臻医学满分通过EMQN室间质评,检测实力再获国际权威机构认可

近日&#xff0c;欧洲分子基因诊断质量联盟&#xff08;European Molecular Genetics Quality Network&#xff0c;EMQN&#xff09;公布了2023年Oncogene panel 项目能力验证考核结果&#xff0c;求臻医学旗下北京和杭州检验实验室&#xff0c;使用自主研发的ChosenOne大Panel…

Apache DolphinScheduler 开源之夏采访:苏国伟的开源之旅

个人介绍 大家好&#xff0c;我是苏国伟&#xff0c;来自西安电子科技大学软件工程专业。我在实验室中主要从事数据集成等方面的工作。除了编程&#xff0c;我还热衷于踢足球、观看球赛和健身&#xff0c;这些爱好让我的生活更加丰富多彩。 开源之路 我最初是在本科的分布式…

【Linux基础开发工具】yum生态vim的配置与使用

目录 前言 1. Linux 软件包管理器 yum 1.1 什么是yum 1.2 快速上手yum 1.3 yum生态 2. Linux编辑器vim 2.1 vim的模式 2.2 vim使用技巧 3. vim编辑器辅助功能配置 3.1 配置 3.2 用户sudo权限配置 总结 前言 Linux基础指令与权限之后&#xff0c;Linux系统开发工具的使用…

美林防火建材——朱林甫 坚韧与创新:美林传奇

在这个变幻莫测的时代&#xff0c;有一些创业者凭借着对行业的深刻理解和对未来的敏锐洞察&#xff0c;不仅在商海中乘风破浪&#xff0c;更是引领了整个行业的发展。今天&#xff0c;我们要讲述的&#xff0c;就是一位这样的创业者——来自浙江嘉善的朱总&#xff0c;他创立的…

初始Redis(入门篇)

目录 什么是Redis Redis特性 速度快 丰富的功能 客户端语言多 持久化 主从复制 Redis可以做什么 缓存 排行榜系统 计数器应用 消息队列系统 Redis安装 centos7安装 Redis重要文件 Redis的使用 Redis通用命令 set get keys exists del expire 什么是Redi…

生成式AI与预测式AI的主要区别与实际应用

近年来&#xff0c;预测式人工智能&#xff08;Predictive AI&#xff09;通过先进的推荐算法、风险评估模型、以及欺诈检测工具&#xff0c;一直在推高着该领域公司的投资回报率。然而&#xff0c;今年初突然杀出的生成式人工智能&#xff08;Generative AI&#xff09;突然成…

如何绕过某讯手游保护系统并从内存中获取Unity3D引擎的Dll文件

​ 某讯的手游保护系统用的都是一套&#xff0c;在其官宣的手游加固功能中有一项宣传是对比较热门的Unity3d引擎的手游保护方案&#xff0c;其中对Dll文件的保护介绍如下&#xff0c; “Dll加固混淆针对Unity游戏&#xff0c;对Dll模块的变量名、函数名、类名进行加密混淆处理&…

Android中添加C或C++代码

1、创建cpp目录&#xff0c;用于存放C和C代码。 2、创建CMake构建脚本CMakeLists.txt文件&#xff0c;将其放在cpp目录中。 # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.22.1)# Declares and names the pro…

单宁对葡萄酒可饮用性和陈酿潜力会有影响吗?

当在酿酒过程中葡萄酒中的单宁过量时&#xff0c;酿酒师可以使用白蛋白、酪蛋白和明胶等各种细化剂&#xff0c;这些药物可以与单宁分子结合&#xff0c;并将其作为沉淀物沉淀出来。随着葡萄酒的老化&#xff0c;单宁将形成长长的聚合链&#xff0c;氧气可以与单宁分子结合&…

数据挖掘实战-基于word2vec的短文本情感分析(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【PPT模板合集】关于自制内容的PPT模板合集,包括原创的PPT及改良内容的PPT,适合科研/比赛/工作

【PPT模板合集】关于自制内容的PPT模板合集&#xff0c;包括原创的PPT及改良内容的PPT&#xff0c;适合科研/比赛/工作 零、前言一、校园层面的PPT模板1.1 各种毕业答辩1.2 夏令营答辩1.3 奖学金答辩1.4 比赛/项目答辩 二、学术层面的PPT模板2.1 学术汇报2.2 会议海报类型 三、…