坑惨啦!!!——符号冲突案例分析

news2024/10/6 18:32:47

 背景

        前段时间在北汽项目中,遇到了一个奇怪现象:程序启动之后,偶现运行一段时间后,crash,复现频率较高。困扰了大家较长时间。最终在和同事的不懈努力下,找到的根因,并找到了解决方法。过程中也学习到了很多。在此,记录并分享,希望能够帮助大家。

问题描述

         作为OTA服务的提供方,我们提供方式一般为将自己的代码编译成动态库(libsysi4dpc.so),提供给设备厂家,让他们进行集成,并调试。

         从控和主控之间通过MQTT协议实现进程间通信。MQTT client相关接口被封装在libupc.a静态库中。也就是说,libsysi4dpc.so 库中其实是包含libupc.a 静态库的所有代码段和数据段(需要的都会加载进去,以.o为单位),并不再依赖其他MQTT 动态库。如下图所示:

         但是当客户集成libsysi4dpc.so 库,生成可执行程序hal-process。当出现crash时,通过gdb进行函数调用栈跟踪。会发现,虽然出错线程的底层调用函数是我们的adm_ups_run 接口(进行MQTT 重连的线程),但是其栈进入到了libmqtt-paho.so库中(这应该也是支持MQTT协议的库)。这就让我们很奇怪,因为我们代码中的MQTT协议接口应该是在libupc.a 中,为什么会走到libmqtt-paho.so 中呢?

         通过查询hal-process 动态库依赖,如下:

        由上可知,libmqtt-paho.so 是hal-process进行链接的。(细心的同事可能会觉得奇怪,为什么没有看到libsysi4dpc.so,那是因为hal-process并不直接依赖我们的库,而是通过libqzm_dpc.so 间接依赖)。

        到此,问题应该就明确了,crash 的原因:是因为adm_ups_run 接口,并没有走到我们自己的mqtt库中,而是运行到了客户提供的mqtt库中,由于mqtt版本的不同,其中兼容问题,导致了crash

项目的模块依赖关系大致如下:

        上述的问题其实有一个专业的名词,叫做同名符号冲突

符号冲突

        符号冲突,作为开发人员,我们应该并不少见。比如编译器提示如下错误:

multiple definition of `xxx';

        其原因,就是同一作用域内,存在相同的符号。可参考C语言中弱符号与弱引用的实际应用_c语言中的弱引用_谢艺华的博客-CSDN博客文章。

        但是为什么上面的场景中,客户在编译hal-process程序时,编译器没有提示相关错误呢?因为符号冲突存在两种形式:显式符号冲突隐式符号冲突

显示符号冲突

        显示符号冲突,就是我们常见的一种错误,它可以在编译阶段直接报错。比如:

yihua@ubuntu:~/symbol$ cat strong_symbol.c
#include<stdio.h>
int symbol()
{
        printf("strong symbol\n");
}
yihua@ubuntu:~/symbol$ cat week_symbol.c
#include<stdio.h>
int symbol()
{
        printf("week symbol\n");
}
yihua@ubuntu:~/symbol$ cat main.c
#include<stdio.h>
#include<stdlib.h>
extern int symbol();
int main()
{
        symbol();
        return 0;
}
yihua@ubuntu:~/symbol$ gcc main.c strong_symbol.c week_symbol.c -o main
/tmp/ccSU1PtA.o: In function `symbol':

week_symbol.c:(.text+0x0): multiple definition of `symbol'
/tmp/cc1gLIic.o:strong_symbol.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

        但有时也存在这样的场景。两个静态库拥有相同的函数符号symbol,并且主函数也调用了symbol,编译阶段会提示错误吗?

还是上述的代码,我们进行如下验证

  1. 生成libstrong.alibweek.a静态库

C
yihua@ubuntu:~/symbol$ gcc -c strong_symbol.c
yihua@ubuntu:~/symbol$ gcc -c week_symbol.c
yihua@ubuntu:~/symbol$ ar -rcs libstrong.a strong_symbol.o
yihua@ubuntu:~/symbol$ ar -rcs libweek.a week_symbol.o

     2. main.c链接两个静态库,生成可执行程序

        由上可知,并没有提示错误,并且输出week symbol

分析:

        我们可以在编译选项中增加-Wl,-–verbose选项(将编译过程中的详细信息进行输出)。输出如下:

yihua@ubuntu:~/symbol$ gcc main.c -L./ -Wl,--verbose -lweek -lstrong -o main
GNU ld (GNU Binutils) 2.30
  Supported emulations:
   elf_x86_64
   elf32_x86_64
   elf_i386
   elf_iamcu
   i386linux
   elf_l1om
   elf_k1om
using internal linker script:
==================================================
/* Script for -pie -z combreloc -z now -z relro: position independent executable, combine & sort relocs */
/* Copyright (C) 2014-2018 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
              "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("/usr/local/x86_64-pc-linux-gnu/lib64"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/x86_64-pc-linux-gnu/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0)); . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rela.dyn       :
    {
      *(.rela.init)
      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
      *(.rela.fini)
      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
      *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
      *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
      *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
      *(.rela.ctors)
      *(.rela.dtors)
      *(.rela.got)
      *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
      *(.rela.ldata .rela.ldata.* .rela.gnu.linkonce.l.*)
      *(.rela.lbss .rela.lbss.* .rela.gnu.linkonce.lb.*)
      *(.rela.lrodata .rela.lrodata.* .rela.gnu.linkonce.lr.*)
      *(.rela.ifunc)
    }
  .rela.plt       :
    {
      *(.rela.plt)
      PROVIDE_HIDDEN (__rela_iplt_start = .);
      *(.rela.iplt)
      PROVIDE_HIDDEN (__rela_iplt_end = .);
    }
  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
.plt.got        : { *(.plt.got) }
.plt.sec        : { *(.plt.sec) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table
  .gcc_except_table.*) }
  .gnu_extab   : ONLY_IF_RO { *(.gnu_extab*) }
  /* These sections are generated by the Sun/Oracle C++ compiler.  */
  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges
  .exception_ranges*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) *(.eh_frame.*) }
  .gnu_extab      : ONLY_IF_RW { *(.gnu_extab) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  .exception_ranges   : ONLY_IF_RW { *(.exception_ranges .exception_ranges*) }
  /* Thread Local Storage sections  */
  .tdata          : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss           : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array     :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
    KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
    PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array     :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
    KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  .got            : { *(.got.plt) *(.igot.plt) *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (0, .);
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  . = .;
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  .lbss   :
  {
    *(.dynlbss)
    *(.lbss .lbss.* .gnu.linkonce.lb.*)
    *(LARGE_COMMON)
  }
  . = ALIGN(64 / 8);
  . = SEGMENT_START("ldata-segment", .);
  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
  }
  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
  {
    *(.ldata .ldata.* .gnu.linkonce.l.*)
    . = ALIGN(. != 0 ? 64 / 8 : 1);
  }
  . = ALIGN(64 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line .debug_line.* .debug_line_end ) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF Extension.  */
  .debug_macro    0 : { *(.debug_macro) }
  .debug_addr     0 : { *(.debug_addr) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}


==================================================
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
attempt to open /tmp/ccipcMTK.o succeeded
/tmp/ccipcMTK.o

attempt to open .//libweek.so failed
attempt to open .//libweek.a succeeded
(.//libweek.a)week_symbol.o
attempt to open .//libstrong.so failed
attempt to open .//libstrong.a succeeded
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open .//libgcc_s.so failed
attempt to open .//libgcc_s.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so succeeded
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
attempt to open libgcc_s.so.1 failed
attempt to open .//libgcc_s.so.1 failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1 succeeded
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open .//libc.so failed
attempt to open .//libc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libc.so succeeded
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libc.so
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/libc.so
attempt to open /lib/x86_64-linux-gnu/libc.so.6 succeeded
/lib/x86_64-linux-gnu/libc.so.6
attempt to open /usr/lib/x86_64-linux-gnu/libc_nonshared.a succeeded
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
attempt to open /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 succeeded
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open .//libgcc_s.so failed
attempt to open .//libgcc_s.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so succeeded
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
opened script file /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so
attempt to open libgcc_s.so.1 failed
attempt to open .//libgcc_s.so.1 failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1 succeeded
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
attempt to open .//libgcc.so failed
attempt to open .//libgcc.a failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.so failed
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/libgcc.a succeeded
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
attempt to open /usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o succeeded
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
ld-linux-x86-64.so.2 needed by /lib/x86_64-linux-gnu/libc.so.6
found ld-linux-x86-64.so.2 at /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2

我们知道静态其实就是目标文件.o的集合体,只有当可执行程序需要时,才会从静态库中提取出相关依赖的.o文件,进行编译。这也是为什么hello world程序依赖libc.a,但是最终可执行程序却比libc.a小的多的原因,因为可执行程序仅将其中的printf.o进行编译。

attempt to open .//libweek.so failed
attempt to open .//libweek.a succeeded
(.//libweek.a)week_symbol.o
attempt to open .//libstrong.so failed
attempt to open .//libstrong.a succeeded

从上述的红色字体中,我们可以知道编译过程:

  1. 加载了libweek.a静态库,并将其中的week_symbol.o提取出来
  2. 加载了libstrong.a静态库,但是没有加载任何目标文件

因此最终输出week symbol这就会导致程序运行最终结果与预期不一致。该现象也称为符号抢占

在如今IT现状下,主进程可能依赖多个模块,各个模块提供的集成方式,很有可能就是以静态库或动态库形式。那么如何确保避免上述问题呢

        链接器提供了一个链接参数-Wl,--whole-archive,该参数告诉链接器,将该参数后面的共享库强制编译到目标文件中,即将共享库中的代码段,数据段强制加入到可执行程序中。对应-Wl,--no-whole-archive关闭该属性。命令行如下:

发现这时提示错误了。打开-Wl,-–verbose参数可知:

attempt to open .//libweek.so failed
attempt to open .//libweek.a succeeded
(.//libweek.a)week_symbol.o
attempt to open .//libstrong.so failed
attempt to open .//libstrong.a succeeded
(.//libstrong.a)strong_symbol.o

此时,加载libstrong.a时,也将strong_symbol.o加载了。

隐式符号冲突

        如上可知,我们遇到的问题并不是显示符号冲突,因为显示符号冲突是在编译阶段就已经确认,并且会一直存在。和我们问题现象不符合。隐式符号冲突会导致程序运行时跳转到错误的符号,进而执行错误的代码段,最终造成错误。

隐式符号冲突其实就是动态库符号重定位导致的问题。可参考我的另一篇博客。

解决方式

        通过上述分析,该问题是隐式符号冲突导致的。最终在编译libsysi4dpc.so动态库时,添加链接属性-Wl,Bsymbolic。要求动态库采用本地的符号。

总结

        通过以上分析,我觉得在项目中,我们可以从预防和解决两个方面规避符号冲,导致程序非预期情况。

  1. 预防,提前识别到冲突符号。使用-Wl,--whole-archive-Wl,--no-whole-archive链接参数。在链接第三方库时,强制将共享库的符号加载到目标文件中。
  2. 若动态库依赖本地的文件时,设置-Wl,Bsymbolic参数。非必要不要使用,会导致目标文件很大

        其实客户编译阶段应该没有设置-Wl,--whole-archive-Wl,--no-whole-archive链接参数,否则在编译阶段应该就会发现该问题。

参考文档

https://blog.csdn.net/weixin_45449806/article/details/129114860

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

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

相关文章

Spring-IOC-@Value和@PropertySource用法

1、Book.java PropertySource(value"classpath:配置文件地址") 替代 <context:property-placeholder location"配置文件地址"/> Value("${book.bid}") Value("${book.bname}") Value("${book.price}") <bean id&…

[羊城杯2020]easyphp .htaccess的利用

[CTF].htaccess的使用技巧总结 例题讲解 掌握知识&#xff1a; 测试发现是阿帕奇服务器&#xff0c;就想到上传文件利用.htaccess配置文件执行jpg文件中的php代码&#xff0c;但是再进行第二次文件写入时会把之前的文件删除掉&#xff0c;所以不能上传两次来利用&#xff0c…

webpack项目 index.html 根据不同的变量引入不同的js

项目 webpack搭建 问题&#xff1a;在入口文件index.html中根据不同的变量引入不同的js 使用插件HtmlWebpackPlugin HtmlWebpackPlugin 项目里用来生成静态文件的 这个插件每个项目基本都要用到的&#xff0c;只要全局搜一下位置 根据配置文件的指令找到执行的文件&#xff0…

openGauss学习笔记-129 openGauss 数据库管理-参数设置-查看参数值

文章目录 openGauss学习笔记-129 openGauss 数据库管理-参数设置-查看参数值129.1 操作步骤129.2 示例 openGauss学习笔记-129 openGauss 数据库管理-参数设置-查看参数值 openGauss安装后&#xff0c;有一套默认的运行参数&#xff0c;为了使openGauss与业务的配合度更高&…

枚举 小蓝的漆房

题目 思路 核心思想是枚举 首先利用set记录每一种颜色 然后依次从set取出一种颜色作为targetColor&#xff0c;遍历房子 如果当前房子的颜色和targetColor不相同&#xff0c;就以当前房子为起点&#xff0c;往后长度为k的区间都涂成targetColor&#xff0c;并且需要的天数递增…

深度学习之基于yolo的体育运动项目姿态估计识别计数系统

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 深度学习在体育运动项目姿态估计识别计数系统中的应用是一项具有挑战性和应用价值的研究领域。以下是对深度学习在体…

Linux下Centos7 gcc/g++、动态库/静态库(动态/静态链接)

1.gcc/g gcc是对c语言代码进行编译链接&#xff0c;而g是对c代码进行编译链接&#xff0c;接下来我们只对gcc进行讲解&#xff0c;g的使用方法跟gcc是一样的。 编译链接的四个步骤: 1:预处理 2:编译 3:汇编 4:链接 注&#xff1a;这些在后面都会着重讲解 1.1gcc -o 我们先在D…

细节决定成败——我的日志去哪了?

概述 编写本文档的目的有两点。 本周遇到了一个日志丢失的问题&#xff0c;经过分析&#xff0c;觉得挺有意思的。向大家分享一下我的分析及解决思路。应该在很多项目中都会有该问题。领导和我私下讨论过多次&#xff0c;当前的autodomain代码对文件读取的频率太高了,如何去避…

Binlog 太大导致无法解析怎么办?

由于业务写入了一条大事务&#xff0c;导致 MySQL 的 binlog 膨胀。在解析大的 binlog 时&#xff0c;经常会遇到这个问题&#xff0c;导致无法解析&#xff0c;没有其他工具的情况下&#xff0c;很难分析问题。 作者&#xff1a;孙绪宗&#xff0c;新浪微博 DBA 团队工程师&am…

【Python】可再生能源发电与电动汽车的协同调度策略研究

1 主要内容 之前发布了《可再生能源发电与电动汽车的协同调度策略研究》matlab版本程序&#xff0c;本次发布的为Python版本&#xff0c;采用gurobi作为求解器&#xff0c;有需要的可以下载对照学习研究。 首先详细介绍了优化调度模型的求解方案&#xff0c;分别采用二次规划…

透视maven打包编译正常,intellj idea编译失败问题的本质

前言 maven多模块类型的项目&#xff0c;在Java的中大型应用中非常常见&#xff0c; 在 module 很多的情况&#xff0c;经常会出现各种各样的编辑依赖错误问题&#xff0c;今天记录一种比较常见的 case &#xff1a; A 子模块依赖 B 子模块&#xff0c;在 Terminal 上终端上 …

LLM之Prompt(二):清华提出Prompt 对齐优化技术BPO

论文题目&#xff1a;《Black-Box Prompt Optimization: Aligning Large Language Models without Model Training》 论文链接&#xff1a;https://arxiv.org/abs/2311.04155 github地址&#xff1a;https://github.com/thu-coai/BPO BPO背景介绍 最近&#xff0c;大型语言模…

Tomcat 9.0.54源码环境搭建

一. 问什么要学习tomcat tomcat是目前非常流行的web容器&#xff0c;其性能和稳定性也是非常出色的&#xff0c;学习其框架设计和底层的实现&#xff0c;不管是使用、性能调优&#xff0c;还是应用框架设计方面&#xff0c;肯定会有很大的帮助 二. 运行源码 1.下载源…

8.2 Windows驱动开发:内核解锁与强删文件

在某些时候我们的系统中会出现一些无法被正常删除的文件&#xff0c;如果想要强制删除则需要在驱动层面对其进行解锁后才可删掉&#xff0c;而所谓的解锁其实就是释放掉文件描述符&#xff08;句柄表&#xff09;占用&#xff0c;文件解锁的核心原理是通过调用ObSetHandleAttri…

【前端】vue中合并表格行

做平台功能时&#xff0c;遇到一个需求是需要将表格某列有相同值时进行合并展示&#xff0c;比如 1、通过在Element中得知需要在表格中增加span-method方法 <el-table:data"tableData":span-method"cellMerge"borderstyle"width: 100%; margin-to…

LeetCode算法心得——打家劫舍(记忆化搜索)

大家好&#xff0c;我是晴天学长&#xff0c;准备开始深入动态规划啦&#xff0c;先从记忆化搜索开始&#xff0c;需要的小伙伴可以关注支持一下哦&#xff01;后续会继续更新的。&#x1f4aa;&#x1f4aa;&#x1f4aa; 1) .打家劫舍 你是一个专业的小偷&#xff0c;计划偷窃…

如何选择适合的开源框架来构建微服务架构?

随着科技的飞速发展&#xff0c;云计算和大规模应用的需求日益显著&#xff0c;这促使微服务架构在软件开发领域中占据了主流地位。微服务架构的广泛应用为开发人员提供了灵活性、可伸缩性和高可用性&#xff0c;从而推动了快速的应用程序开发。然而&#xff0c;在构建微服务架…

React函数组件渲染两次

渲染两次是因为react默认开启了严格模式 React.StrictMode标签作用&#xff1a; 1、识别不安全的生命周期 2、关于使用过时字符串 ref API 的警告 3、关于使用废弃的 findDOMNode 方法的警告 4、检测意外的副作用 5、检测过时的 context API 注释掉React.StrictMode即为关闭严…

2024测试工程师必学系列之Jmeter(36):jmeter对图片验证码的处理

jmeter对图片验证码的处理 在web端的登录接口经常会有图片验证码的输入&#xff0c;而且每次登录时图片验证码都是随机的&#xff1b;当通过jmeter做接口登录的时候要对图片验证码进行识别出图片中的字段&#xff0c;然后再登录接口中使用&#xff1b; 通过jmeter对图片验证码…

【蓝桥杯省赛真题44】Scratch像素画板 蓝桥杯少儿编程scratch图形化编程 蓝桥杯省赛真题讲解

scratch像素画板 第十四届青少年蓝桥杯scratch编程省赛真题 一、题目要求 编程实现 1.点击绿旗,角色、背景如图所示(三种颜色调色盘、清除图标及方格角色请自行创建,点击绿旗后立刻呈现下图效果); 2.用鼠标点击红色调色盘,红色调色盘变为选中状态(如下图所示),此时鼠…