简体   繁体   English

构建用于 Nordic SDK 程序的静态 Rust 库时对 `memset' 的多重定义

[英]multiple definition of `memset' when building static rust library to be used for Nordic SDK program

Note: I do not see that this is a duplicate of the other question.注意:没有看到这是另一个问题的重复。 It's in my question, that I checked the suggetion provided there and this doesn't help here (as shown below).在我的问题中,我检查了那里提供的建议,但这在这里没有帮助(如下所示)。 If you see this differently, I would like to know which kind of information you are thinking of, when you write “If the solution does not work, you should make clear why not and what you have tried to solve it already.”如果你有不同的看法,我想知道你在想什么类型的信息,当你写“如果解决方案不起作用,你应该清楚为什么不可行以及你已经尝试解决了什么问题。” I'm happy to provide everything that is needed to understand the problem.我很高兴提供了解问题所需的一切。

That's way I wrote this question to ask for additional suggestions what I can test or where I could search for additional solutions.这就是我写这个问题的方式,以询问我可以测试什么或我可以在哪里搜索其他解决方案的其他建议。

I know that this question is a long one.我知道这个问题很长。 But the text I wrote is actually as short as possible.但是我写的文字实际上是尽可能短的。 It's just the shell output I provide, that makes it long.这只是我提供的 shell 输出,所以它很长。 So I think it actually is quite readable.所以我认为它实际上很有可读性。

I try to build a static library in Rust, that I want to use in an embedded program, that runs on a Nordic nRF52832 bluetooth low energy micro controller.我尝试在 Rust 中构建一个静态库,我想在嵌入式程序中使用它,该程序在 Nordic nRF52832 蓝牙低功耗微控制器上运行。

The problem is that the static library that Rust generates contains symbols, that are also defined by libc_nano:问题是 Rust 生成的静态库包含符号,这些符号也是由 libc_nano 定义的:

/usr/lib/gcc/arm-none-eabi/7.3.1/../../../arm-none-eabi/bin/ld: ../../modem/target/thumbv7em-none-eabihf/release/libtbmodem.a(compiler_builtins-ace0cbd4c713eeda.compiler_builtins.8gdoevk6-cgu.12.rcgu.o): in function `memset':
/checkout/src/rustc/compiler_builtins_shim/../../libcompiler_builtins/src/mem.rs:47: multiple definition of `memset'; /usr/lib/gcc/arm-none-eabi/7.3.1/../../../arm-none-eabi/lib/thumb/v7e-m/fpv4-sp/hard/libc_nano.a(lib_a-memset.o):/build/newlib-4qXI0C/newlib-3.0.0.20180802/build_nano/arm-none-eabi/thumb/v7e-m/fpv4-sp/hard/newlib/libc/string/../../../../../../../../../newlib/libc/string/memset.c:41: first defined here
collect2: error: ld returned 1 exit status

Looking at the output of nm is see, that indeed the library compiled by rust does define memset and some other typical libc functions:查看nm的输出可以看到,rust 编译的库确实定义了memset和其他一些典型的 libc 函数:

$ nm target/thumbv7em-none-eabihf/release/libtbmodem.a|grep mem
         U __aeabi_memclr
         U __aeabi_memcpy
         U __aeabi_memclr
         U __aeabi_memcpy
         U __aeabi_memcpy
         U __aeabi_memmove
         U __aeabi_memcpy
         U __aeabi_memcpy
         U __aeabi_memmove
         U __aeabi_memcpy
         U memcmp
         U __aeabi_memcpy
00000000 T memcmp
00000000 T memcpy
00000000 T memmove
00000000 T memset
00000000 W __aeabi_memclr
00000000 W __aeabi_memclr4
00000000 W __aeabi_memclr8
00000000 W __aeabi_memcpy
00000000 W __aeabi_memcpy4
00000000 W __aeabi_memcpy8
00000000 W __aeabi_memmove
00000000 W __aeabi_memmove4
00000000 W __aeabi_memmove8
00000000 W __aeabi_memset
00000000 W __aeabi_memset4
00000000 W __aeabi_memset8
         U memcmp
00000000 T _ZN4core5slice6memchr6memchr17h3803f373e8c2e0b4E
00000000 T _ZN4core5slice6memchr7memrchr17hed16b4d75d82bdf8E
         U __aeabi_memcpy
         U memcmp
         U _ZN4core5slice6memchr6memchr17h3803f373e8c2e0b4E
         U __aeabi_memclr4
         U __aeabi_memcpy4
         U memcmp
         U __aeabi_memcpy
         U __aeabi_memset
         U memcmp
         U _ZN4core5slice6memchr6memchr17h3803f373e8c2e0b4E
         U __aeabi_memclr4
         U __aeabi_memcpy4
         U __aeabi_memset
         U __aeabi_memclr4
         U __aeabi_memcpy4
         U memcmp

Interestingly these colliding symbols aren't generated by Rust, when I compile for my host architecture x86_64-unknown-linux-gnu :有趣的是,当我为我的主机架构x86_64-unknown-linux-gnu编译时,这些碰撞符号不是由 Rust 生成的:

$ nm target/x86_64-unknown-linux-gnu/release/libtbmodem.a |grep mem
                 U memcpy
                 U memset
                 U memcpy
                 U memset
                 U memcpy
                 U memmove
                 U memcpy
                 U memcpy
                 U memmove
                 U memcmp
                 U memcpy
                 U memcpy
0000000000000000 T _ZN17compiler_builtins3mem6memcmp17hd848cdf5fbc51bd1E
0000000000000000 T _ZN17compiler_builtins3mem6memcpy17he0a8277ca9da5208E
0000000000000000 T _ZN17compiler_builtins3mem6memset17h0fcc19dd786ae994E
0000000000000000 T _ZN17compiler_builtins3mem7memmove17h75c47f31c61a641fE
0000000000000000 T _ZN4core5slice6memchr6memchr17h3803f373e8c2e0b4E
0000000000000000 T _ZN4core5slice6memchr7memrchr17hed16b4d75d82bdf8E
                 U memcmp
                 U memcpy
                 U _ZN4core5slice6memchr6memchr17h3803f373e8c2e0b4E
                 U memcmp
                 U memcpy
                 U memset
                 U memcmp
                 U memcpy
                 U _ZN4core5slice6memchr6memchr17h3803f373e8c2e0b4E
                 U memcpy
                 U memset
                 U memcmp
                 U memcpy

What is the reason for Rust to define these symbols when compiling for ARM but not when compiling for x86_64? Rust 在为 ARM 编译时定义这些符号而不是在为 x86_64 编译时定义这些符号的原因是什么?

I know, that the same error is also discussed in "multiple definition of `memcmp" error when linking Rust staticlib with embedded C program , but the solution provided there doesn't work for me.我知道, 在将 Rust staticlib 与嵌入式 C 程序链接时,“memcmp 的多重定义”错误中也讨论了相同的错误,但那里提供的解决方案对我不起作用。 Actually I already have the static library produced by rust as the last argument to the linker call:实际上,我已经将 rust 生成的静态库作为链接器调用的最后一个参数:

arm-none-eabi-gcc -DBUILD=\\" date +%F \\" -DBUILDY= date +%Y -DBUILDM= date +%-m -DBUILDD= date +%-d -mcpu=cortex-m4 -mthumb -mabi=aapcs -mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -Iconfig -std=c99 -DNRF52 -DNRF52832_XXAA -DBOARD_CUSTOM -DBLE_STACK_SUPPORT_REQD -DNRF_SD_BLE_API_VERSION=6 -DNRF_DFU_SVCI_ENABLED -DNRF_DFU_TRANSPORT_BLE=1 -DS132 -DSOFTDEVICE_PRESENT -DSWI_DISABLE0 -Wall -Werror -DFLOAT_ABI_HARD -DCONFIG_GPIO_AS_PINRESET -DNRF52_PAN_74 -I/home/matthias/source/tbblue/libtrailer/src @sdk_include_paths -Og -g3 -DDEBUG -DDEBUG_NRF -mthumb -mabi=aapcs -mcpu=cortex-m4 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=tbplatform.map,--wrap,_sbrk -L/home/matthias/source/tbblue/libtrailer/src -L /home/matthias/source/tbblue/nRF5_SDK//components/toolchain/gcc -T tbplatform.ld -o tbplatform .sdk/modules/nrfx/mdk/gcc_startup_nrf52.o nrfplatform.o base64.o bleinithelper.o ble_dfu_service.o broadcast. arm-none-eabi-gcc -DBUILD=\\" date +%F \\" -DBUILDY= date +%Y -DBUILDM= date +%-m -DBUILDD= date +%-d -mcpu=cortex-m4 -mthumb - mabi=aapcs -mfloat-abi=hard -mfpu=fpv4-sp-d16 -ffunction-sections -fdata-sections -fno-strict-aliasing -fno-builtin --short-enums -Iconfig -std=c99 -DNRF52 -DNRF52832_XXAA -DBOARD_CUSTOM -DBLE_STACK_SUPPORT_REQD -DNRF_SD_BLE_API_VERSION = 6 -DNRF_DFU_SVCI_ENABLED -DNRF_DFU_TRANSPORT_BLE = 1 -DS132 -DSOFTDEVICE_PRESENT -DSWI_DISABLE0 -Wall -Werror -DFLOAT_ABI_HARD -DCONFIG_GPIO_AS_PINRESET -DNRF52_PAN_74 -I /家/马提亚/源极/ tbblue / libtrailer / SRC @sdk_include_paths -OG -g3 -DDEBUG -DDEBUG_NRF -mthumb -mabi=aapcs -mcpu=cortex-m4 -Wl,--gc-sections --specs=nano.specs -lc -lnosys -lm -Wl,-Map=tbplatform.map,--wrap ,_sbrk -L/home/matthias/source/tbblue/libtrailer/src -L /home/matthias/source/tbblue/nRF5_SDK//components/toolchain/gcc -T tbplatform.ld -o tbplatform .sdk/modules/nrfx/ mdk/gcc_startup_nrf52.o nrfplatform.o base64.o bleinithelper.o ble_dfu_service.o 广播。 o config_service.o fw_version.o main.o mac_address.o modem.o nrf_ble_qwrs.o on_board_flash.o service.o system.o compartment_util.o saved_config.o temperature_util.o tick_util.o digitalinput/digital.o spi/spi.o spi/can/can_controller.o spi/can/can_interrupt.o spi/can/can_timer.o spi/can/can.o spi/uart/spi_uart.o uart/datacold500_uart.o uart/datacold600_uart.o uart/euroscan_uart.o uart/ibox_uart.o uart/uart.o .sdk/components/ble/ble_advertising/ble_advertising.o .sdk/components/ble/ble_services/ble_dfu/ble_dfu.o .sdk/components/ble/ble_services/ble_dfu/ble_dfu_unbonded.o .sdk/components/ble/common/ble_advdata.o .sdk/components/ble/common/ble_conn_params.o .sdk/components/ble/common/ble_conn_state.o .sdk/components/ble/common/ble_srv_common.o .sdk/components/ble/nrf_ble_gatt/nrf_ble_gatt.o .sdk/components/ble/peer_manager/gatt_cache_manager.o .sdk/components/ble/peer_manager/gatts_cache_manager.o .sdk/components/ble/peer_manager/id_manager.o .sdk/components/ble/peer_manager/peer_database. o config_service.o fw_version.o main.o mac_address.o modem.o nrf_ble_qwrs.o on_board_flash.o service.o system.o 隔间_util.o saved_config.o temperature_util.o tick_util.o digitalinput/digital.o spi/spi.o spi/can/can_controller.o spi/can/can_interrupt.o spi/can/can_timer.o spi/can/can.o spi/uart/spi_uart.o uart/datacold500_uart.o uart/datacold600_uart.o uart/euroscan_uart.o uart/ibox_uart.o uart/uart.o .sdk/components/ble/ble_advertising/ble_advertising.o .sdk/components/ble/ble_services/ble_dfu/ble_dfu.o .sdk/components/ble/ble_services/ble_dfu/ble_advertising.o .sdk/components/ble/ble_services/ble_dfu/ble_dfu.o .sdk/components/ble/ble_services/ble_dfu/ble_advertising.o .sdk/components/ble/common/ble_advdata.o .sdk/components/ble/common/ble_conn_params.o .sdk/components/ble/common/ble_conn_state.o .sdk/components/ble/common/ble_srv_common.o .sdk /components/ble/nrf_ble_gatt/nrf_ble_gatt.o .sdk/components/ble/peer_manager/gatt_cache_manager.o .sdk/components/ble/peer_manager/gatts_cache_manager.o .sdk/components/ble/peer_manager/gatt_cache_manager.o /ble/peer_manager/peer_database. o .sdk/components/ble/peer_manager/peer_data_storage.o .sdk/components/ble/peer_manager/peer_id.o .sdk/components/ble/peer_manager/peer_manager.o .sdk/components/ble/peer_manager/pm_buffer.o .sdk/components/ble/peer_manager/pm_mutex.o .sdk/components/ble/peer_manager/security_dispatcher.o .sdk/components/ble/peer_manager/security_manager.o .sdk/components/boards/boards.o .sdk/components/libraries/atomic/nrf_atomic.o .sdk/components/libraries/atomic_fifo/nrf_atfifo.o .sdk/components/libraries/atomic_flags/nrf_atflags.o .sdk/components/libraries/balloc/nrf_balloc.o .sdk/components/libraries/bootloader/dfu/nrf_dfu_svci.o .sdk/components/libraries/crc16/crc16.o .sdk/components/libraries/experimental_log/src/nrf_log_default_backends.o .sdk/components/libraries/experimental_log/src/nrf_log_backend_rtt.o .sdk/components/libraries/experimental_log/src/nrf_log_backend_serial.o .sdk/components/libraries/experimental_log/src/nrf_log_backend_uart.o .sdk/components/libraries/experimental_log/src/nr o .sdk/components/ble/peer_manager/peer_data_storage.o .sdk/components/ble/peer_manager/peer_id.o .sdk/components/ble/peer_manager/peer_manager.o .sdk/components/ble/peer_buffer.pm sdk/components/ble/peer_manager/pm_mutex.o .sdk/components/ble/peer_manager/security_dispatcher.o .sdk/components/ble/peer_manager/security_manager.o .sdk/components/boards/boards.o .sdk/components图书馆/原子/nrf_atomic.o .sdk/components/libraries/atomic_fifo/nrf_atfifo.o .sdk/components/libraries/atomic_flags/nrf_atflags.o .sdk/components/libraries/balloc/nrf_ballocaries/component/slibraries/引导加载程序/dfu/nrf_dfu_svci.o .sdk/components/libraries/crc16/crc16.o .sdk/components/libraries/experimental_log/src/nrf_log_default_backends.o .sdk/components/libraries/experimental.rfdsrdk.组件/库/experimental_log/src/nrf_log_backend_serial.o .sdk/components/libraries/experimental_log/src/nrf_log_backend_uart.o .sdk/components/libraries/experimental_log/src/nr f_log_frontend.o .sdk/components/libraries/experimental_log/src/nrf_log_str_formatter.o .sdk/components/libraries/experimental_memobj/nrf_memobj.o .sdk/components/libraries/experimental_section_vars/nrf_section_iter.o .sdk/components/libraries/fds/fds.o .sdk/components/libraries/fifo/app_fifo.o .sdk/components/libraries/fstorage/nrf_fstorage.o .sdk/components/libraries/fstorage/nrf_fstorage_sd.o .sdk/components/libraries/pwr_mgmt/nrf_pwr_mgmt.o .sdk/components/libraries/strerror/nrf_strerror.o .sdk/components/libraries/timer/app_timer.o .sdk/components/libraries/uart/app_uart_fifo.o .sdk/components/libraries/uart/retarget.o .sdk/components/libraries/util/app_error_weak.o .sdk/components/libraries/util/app_util_platform.o .sdk/components/softdevice/common/nrf_sdh.o .sdk/components/softdevice/common/nrf_sdh_ble.o .sdk/components/softdevice/common/nrf_sdh_soc.o .sdk/external/fprintf/nrf_fprintf.o .sdk/external/fprintf/nrf_fprintf_format.o .sdk/external/segger_rtt/SEGGER_RTT.o .sdk/externa f_log_frontend.o .sdk/components/libraries/experimental_log/src/nrf_log_str_formatter.o .sdk/components/libraries/experimental_memobj/nrf_memobj.o .sdk/components/libraries/experimental_section_components.fdlibraries/experimental_section_components/flibraries fds.o .sdk/components/libraries/fifo/app_fifo.o .sdk/components/libraries/fstorage/nrf_fstorage.o .sdk/components/libraries/fstorage/nrf_fstorage_sd.o .sdk/components/mg_wrfmtmpm o .sdk/components/libraries/strerror/nrf_strerror.o .sdk/components/libraries/timer/app_timer.o .sdk/components/libraries/uart/app_uart_fifo.o .sdk/components/libraries/uart/retarget.o . sdk/components/libraries/util/app_error_weak.o .sdk/components/libraries/util/app_util_platform.o .sdk/components/softdevice/common/nrf_sdh.o .sdk/components/softdevice/common/nrf_sdh_ble.o .sdk组件/softdevice/common/nrf_sdh_soc.o .sdk/external/fprintf/nrf_fprintf.o .sdk/external/fprintf/nrf_fprintf_format.o .sdk/external/segger_rtt/SEGGER_RTT.o .sdk/ l/segger_rtt/SEGGER_RTT_Syscalls_GCC.o .sdk/external/segger_rtt/SEGGER_RTT_printf.o .sdk/external/tiny-AES128/aes.o .sdk/integration/nrfx/legacy/nrf_drv_spi.o .sdk/integration/nrfx/legacy/nrf_drv_uart.o .sdk/modules/nrfx/drivers/src/nrfx_gpiote.o .sdk/modules/nrfx/drivers/src/nrfx_spis.o .sdk/modules/nrfx/drivers/src/nrfx_uart.o .sdk/modules/nrfx/drivers/src/nrfx_uarte.o .sdk/modules/nrfx/drivers/src/prs/nrfx_prs.o .sdk/modules/nrfx/drivers/src/nrfx_spim.o .sdk/modules/nrfx/mdk/system_nrf52.o -ltrailer ../../modem/target/thumbv7em-none-eabihf/release/libtbmodem.a l/segger_rtt/SEGGER_RTT_Syscalls_GCC.o .sdk/external/segger_rtt/SEGGER_RTT_printf.o .sdk/external/tiny-AES128/aes.o .sdk/integration/nrfx/legacy/nrf_drvs/integration/nrf_drvsdspi nrf_drv_uart.o .sdk/modules/nrfx/drivers/src/nrfx_gpiote.o .sdk/modules/nrfx/drivers/src/nrfx_spis.o .sdk/modules/nrfx/drivers/src/nrfx_uart.o nrfx/drivers/src/nrfx_uarte.o .sdk/modules/nrfx/drivers/src/prs/nrfx_prs.o .sdk/modules/nrfx/drivers/src/nrfx_spim.o .sdk/modules/nrfx.mrfk/system o -ltrailer ../../modem/target/thumbv7em-none-eabihf/release/libtbmodem.a

(It's the file libtbmodem.a .) (这是文件libtbmodem.a 。)

What else can I do to get the library linked?我还能做些什么来链接图书馆?

I had the same problem as you today and I can offer a possible answer (it works in my case) and a partial response why this works:我今天遇到了和你一样的问题,我可以提供一个可能的答案(它适用于我的情况)以及为什么它有效的部分回应:

TL;DR: My solution was to place my Rust library before libc on the linker line. TL;DR:我的解决方案是将 Rust 库放在链接器行上的libc之前 In your case, this would mean to change your call to:在您的情况下,这意味着将您的电话更改为:

arm-none-eabi-gcc [...] specs=nano.specs -ltbmodem -lc -lnosys [...]

Explanation: Examining my Rust library shows, as in your case, that symbols for memcpy , memset and so forth are exported from my static library.说明:检查我的 Rust 库显示,与您的情况一样, memcpymemset等符号是从我的静态库中导出的。 Note that those are contained in an object with several other memory related functions:请注意,这些包含在具有其他几个与内存相关的功能的对象中:

$ nm librustlib.a
[...]
compiler_builtins-17b2875688712538.compiler_builtins.eth5fjl6-cgu.100.rcgu.o:
00000000 T bcmp
00000000 T memcmp
00000000 T memcpy
00000000 T memmove
00000000 T memset
00000000 T _ZN17compiler_builtins3mem40__llvm_memcpy_element_unordered_atomic_117h2c6de8c0ed769de1E
[... seven "compiler_builtins" functions more omitted ...]
00000000 T _ZN17compiler_builtins3mem41__llvm_memmove_element_unordered_atomic_417h971099a5edfec01eE
[...]

Some of those functions are also exported from libc , for example:其中一些函数也从libc导出,例如:

$ nm /usr/arm-none-eabi/lib/thumb/v7e-m+fp/hard/libc_nano.a
[...]
lib_a-memcpy.o:

lib_a-memcpy-stub.o:
00000000 t $t
00000001 T memcpy
[...]

Specifically, the five functions without name-mangling (the functions with normal names) can be found in libc_nano.a .具体来说,可以在libc_nano.a找到没有名称修改的五个函数(具有正常名称的函数)。

To understand if link order makes a difference here, I have read Library Order in Static Linking which contains the following interesting section:要了解链接顺序在这里是否有所不同,我阅读了静态链接中的库顺序,其中包含以下有趣的部分:

When the linker encounters a new library, things are a bit more interesting.当链接器遇到一个新的库时,事情就变得更有趣了。 The linker goes over all the objects in the library.链接器遍历库中的所有对象。 For each one, it first looks at the symbols it exports.对于每一个,它首先查看它导出的符号。

  • If any of the symbols it exports are on the undefined list, the object is added to the link and the next step is executed.如果它导出的任何符号在未定义列表中,则该对象将添加到链接中并执行下一步。 Otherwise, the next step is skipped.否则,跳过下一步。
  • If the object has been added to the link, it's treated as described above - its undefined and exported symbols get added to the symbol table.如果对象已添加到链接,则按上述方式处理 - 其未定义和导出的符号将添加到符号表中。
  • Finally, if any of the objects in the library has been included in the link, the library is rescanned again - it's possible that symbols imported by the included object can be found in other objects within the same library.最后,如果库中的任何对象已包含在链接中,则会再次重新扫描库 - 由包含的对象导入的符号可能会在同一库中的其他对象中找到。

So when the linker scans a library, every contained object is handled separately and can be used or be omitted from the final result, depending on whether it seems to be required to the linker, ie whether it defines a symbol that is still undefined.因此,当链接器扫描一个库时,每个包含的对象都被单独处理,可以使用或从最终结果中省略,这取决于链接器是否需要它,即它是否定义了一个仍未定义的符号。 In the article above, this is stated as:在上面的文章中,这是这样表述的:

Also note that when a library is examined, an object file within it can be left out of the link if it does not provide symbols that the symbol table needs.另请注意,在检查库时,如果其中的目标文件不提供符号表所需的符号,则可以将其排除在链接之外。 This is a very important feature of static linking.这是静态链接的一个非常重要的特性。 The C library I mentioned before makes a heavy use of this feature, by mostly splitting itself to an-object-per-function.我之前提到的 C 库大量使用了这个特性,主要是将自身拆分为一个对象每个函数。

For linking my Rust library with C code (and thus libc ) this means specifically:为了将我的 Rust 库与 C 代码(以及libc )链接,这特别意味着:

  • If the Rust library is placed earlier than libc on the command-line, the "compiler_builtins"-object defining the memory-related functions will be linked in the final result.如果 Rust 库在命令行中比libc更早放置,则定义内存相关函数的“compiler_builtins”对象将在最终结果中链接。 When libc is examined afterwards, the memory-related functions are already defined so that the corresponding objects from libc will be omitted.之后检查libc ,已经定义了与内存相关的函数,因此将省略libc中的相应对象。
  • If the Rust library is placed later on the command-line, the memory-related functions from libc will be used.如果稍后将 Rust 库放在命令行中,则将使用libc与内存相关的函数。 Then, the linker also uses the object from the Rust library, most probably because of the name-mangled functions are required.然后,链接器使用 Rust 库中的对象,很可能是因为需要名称修饰函数。 This leaves us with having memcpy and the like defined twice which is an error.这让我们不得不将memcpy等定义两次,这是一个错误。

I hope this information is still helpful or can be helpful to anyone who stumbles upon the same problem in the future.我希望这些信息仍然有用,或者可以对将来偶然发现相同问题的任何人有所帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM