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

news2024/9/23 19:26:20

绪论

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

简介

        从上文【程序员的自我修养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段,符号表等内容,以及工作中可以借鉴的地方。也初步了解到了重定位表的存在及含义,为后面的静态链接做好了铺垫。

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python 异常处理(try except)

文章目录 1 概述1.1 异常示例 2 异常处理2.1 捕获异常 try except2.2 抛出异常 raise 3 异常类型3.1 内置异常3.2 自定义异常 1 概述 1.1 异常示例 异常&#xff1a;程序执行中出现错误&#xff0c;若不处理&#xff0c;则程序终止 示例代码&#xff1a; v 6 / 0 # 除数不…

滴水逆向三期笔记与作业——02C语言——07 多维数组

OneNote防丢 多维数组 一、二维数组的初始化1.1 二维数组初始化1.2 给定部分值1.3 二维数组省略第二层的{}1.4 省略长度1.5 多维数组寻址1.6 作业 一、二维数组的初始化 1.1 二维数组初始化 代码1 int arr[3][4] { {1,2,3,4}, {5,6,7,8}, {9,7,6,5} }; 代码2 int arr[3*4…

科普:什么是合同生命周期管理?

在当前&#xff0c;企业面临自身转型升级与行业数字建设的挑战&#xff0c;急需一种系统化的解决方案&#xff0c;帮助企业在自身运作方面实现降本增效&#xff0c;为拓展业务获得发展提供助力&#xff0c;而合同生命周期管理在其中就扮演着十分重要的角色。 一、合同生命周期…

超详细!Opencv人脸识别!附源码!

一、新建环境 注意&#xff01;&#xff01;确定后需要关闭项目&#xff0c;重新打开&#xff0c;终端的环境才会变化&#xff01;&#xff01; 二、下载安装包&#xff08;只需要3个即可&#xff09; 1. 下载dlib包 pip install dlib-19.19.0-cp38-cp38-win_amd64.whl.whl …

数据结构学习笔记——二叉树的遍历和链式存储代码实现二叉树

目录 一、二叉树的遍历&#xff08;一&#xff09;二叉树的先序遍历&#xff08;DLR&#xff09;&#xff08;二&#xff09;二叉树的中序遍历&#xff08;LDR&#xff09;&#xff08;三&#xff09;二叉树的后序遍历&#xff08;LRD&#xff09;&#xff08;四&#xff09;二…

修复电脑中缺失的VCRUNTIME140.dll文件的5个有效方法

vcruntime140.dll丢失5个修复方法与vcruntime140.dll是什么以及丢失对电脑的影响 引言&#xff1a; 在日常使用电脑的过程中&#xff0c;我们可能会遇到一些错误提示&#xff0c;其中之一就是“vcruntime140.dll丢失”。那么&#xff0c;什么是vcruntime140.dll&#xff1f;它…

虚幻学习笔记1—给UI添加动画

一、前言 本文所使用的虚幻版本为5.3.2&#xff0c;之前工作都是用unity&#xff0c;做这类效果用的最多的是一个DoTween的插件&#xff0c;在虚幻中都内置集成了这这种效果制作。 图1.1 UI动画 二、过程 1、首先&#xff0c;在诸如按钮、图像等可交互控件中选中&#xff0c;如…

【驱动】串口驱动分析(四)-串口编程和调试方法

串口调试 串口调试主要有 根据/proc系统信息确认串口状态&#xff0c;stty命令&#xff0c;编程调试 三种调试方法&#xff0c;下面我们分别具体介绍下。 根据设备节点确认串口是否正常 系统上电时&#xff0c;默认会使能串口&#xff0c;我们可以通过dmesg | grep ttyS 查看…

2023 年 IntelliJ IDEA下载、安装教程,附详细图文

大家好&#xff0c;今天为大家带来的是 2023年 IntelliJ IDEA 下载、安装教程&#xff0c;超详细的图文教程&#xff0c;亲测可用。 文章目录 1 IDEA 下载2 IDEA 安装3 IDEA 使用4 快捷键新手必须掌握&#xff1a;Ctrl&#xff1a;Alt&#xff1a;Shift&#xff1a;Ctrl Alt&a…

mysql:免费的GUI客户端工具推荐并介绍常用的操作

给大家推荐几个常用的 mysql 数据库客户端 sequel-pro sequel-ace 官网下载地址 免费 sequel-ace 可以理解为 Sequel Pro 的升级版&#xff0c;由于Sequel Pro官方不维护了&#xff0c;特别是对 MySQL 8.0 支持不好&#xff0c;所以现在由社区维护了新分支 sequel-ace&#x…

中兴亮相中国国际现代化铁路技术装备展览会 筑智铁路5G同行

近日&#xff0c;第十六届中国国际现代化铁路技术装备展览会在北京中国国际展览中心举办&#xff0c;中兴以“数智铁路&#xff0c;5G同行”主题亮相本次展览会&#xff0c;并全面展示了“数字铁路网络基础设施”、“云边结合的铁路行业云”、“数字铁路赋能赋智”等方面的最新…

[蓝桥杯习题]———位运算、判断二进制1个数

⭐Hello!这里是欧_aita的博客。 ⭐今日语录&#xff1a;行动胜过一切。 ⭐个人主页&#xff1a;欧_aita ψ(._. )>⭐个人专栏&#xff1a; 数据结构与算法&#xff08;内含蓝桥杯习题&#xff09; MySQL数据库 位运算 位运算位运算的定义简单运用 实战刷题题目思路代码实现声…

第十八章,Swing窗体

概述 String包的层次结构和继承关系如下 常用的Swing组件如下表 Swing常用窗体 JFrame 窗体 JFrame 类的常用构造方法包括以下两种形式&#xff1a; public JFrame&#xff08;&#xff09;&#xff1a;创建一个初始不可见、没有标题的窗体。 public JFrame(String title)&a…

调查问卷平台可以设置考卷吗?在线考试与问卷调查的结合

我们现在使用调查问卷不仅可以用来做数据收集&#xff0c;还可以用来设置考卷。 Zoho Survey也可以帮助您实现考试这一功能&#xff0c;具体操作如下图&#xff1a; 第一步&#xff1a;打开【Survey】&#xff0c;点击【创建调查】。 第二步&#xff1a;填写【调查名称】和【类…

中国毫米波雷达产业分析3——毫米波雷达市场分析(四、五、六)

四、康养雷达市场 &#xff08;一&#xff09;市场背景 1、政府出台系列政策提升智慧健康养老产品供给和应用 康养雷达是一种以老年人为主要监测对象&#xff0c;可以实现人体感应探测、跌倒检测报警、睡眠呼吸心率监测等重要养老监护功能的新型智慧健康养老产品。 随着我国经…

.net core 连接数据库,通过数据库生成Modell

1、安装EF Core Power Tools&#xff1a;打开Vs开发工具→扩展→管理扩展 2、(切记执行这步之前确保自己的代码不存在编写或者编译错误&#xff01;)安装完成后在你需要创建数据库实体的项目文件夹上面单击右键&#xff0c;找到EF Core 工具&#xff08;必须安装扩展之和才会有…

深度解析 Spring Security 自定义异常失效问题:源码剖析与解决优化

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

ACM程序设计课内实验(2) 排序问题

基础知识‘ sort函数 C中的sort函数是库中的一个函数&#xff0c;用于对容器中的元素进行排序。它的原型如下&#xff1a; template <class RandomAccessIterator, class Compare> void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);参数…

JAVA基础进阶(三)

一、权限修饰符的访问权限 需要特别注意的是: 被private修饰的成员变量以及成员方法只能在本类中进行调用&#xff0c;所以在其他类中创建本类对象,无法直接访问私有成员变量和成员方法,只能通过set、get方法间接访问。被public修饰的成员变量以及成员方法可以在任意地方被调用…

Linux C/C++高级全栈开发(后端/游戏/嵌入式/高性能网络/存储/基础架构)

Linux C/C高级全栈开发是一个涉及到多个领域的综合性技术要求&#xff0c;需要对Linux系统、C/C编程语言以及各种相关的技术进行深入的理解和应用。 下面是一些涵盖的主要技术领域和技能要点&#xff1a; Linux系统基础&#xff1a;熟悉Linux操作系统的原理和常用命令&#xf…