0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

u-boot armv8鏈接腳本

麥辣雞腿堡 ? 來(lái)源:CSDN博客 ? 作者:內(nèi)核新視界 ? 2023-12-07 11:19 ? 次閱讀

u-boot armv8鏈接腳本

在進(jìn)行源碼分析之前,首先看看u-boot的鏈接腳本,通過(guò)鏈接腳本可以從整體了解一個(gè)u-boot的組成,并且可以在啟動(dòng)分析中知道某些邏輯是在完成什么工作。

在armv8中,u-boot使用arch/arm/cpu/armv8/u-boot.lds進(jìn)行鏈接。

u-boot-spl和u-boot-tpl使用arch/arm/cpu/armv8/u-boot-spl.lds進(jìn)行鏈接,因?yàn)槊總€(gè)board的情況可能不同,所以u(píng)-boot可以通過(guò)Kconfig來(lái)自定義u-boot-spl.lds和u-boot-tpl.lds。

4.1 u-boot.lds

/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * (C) Copyright 2013
 * David Feng < fenghua@phytium.com.cn >
 *
 * (C) Copyright 2002
 * Gary Jennejohn, DENX Software Engineering, < garyj@denx.de >
 */

#include < config.h >
#include < asm/psci.h >

OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start) -------------------------------------------------------------------- (1)
/*
 *(1)首先定義了二進(jìn)制程序的輸出格式為"elf64-littleaarch64",
 *    架構(gòu)是"aarch64",程序入口為"_start"符號(hào);
 */
SECTIONS
{
#ifdef CONFIG_ARMV8_SECURE_BASE -------------------------------------------------- (2)
/*
 *(2)ARMV8_SECURE_BASE是u-boot對(duì)PSCI的支持,在定義時(shí)可以將PSCI的文本段,
 *    數(shù)據(jù)段,堆棧段重定向到指定的內(nèi)存,而不是內(nèi)嵌到u-boot中。
 *    不過(guò)一般廠商實(shí)現(xiàn)會(huì)使用atf方式使其與bootloader分離,這個(gè)功能不常用;
 */
 /DISCARD/ : { *(.rela._secure*) }
#endif
 . = 0x00000000; -------------------------------------------------------------- (3)
/*
 *(3)定義了程序鏈接的基地址,默認(rèn)是0,通過(guò)配置CONFIG_SYS_TEXT_BASE可修改
 *    這個(gè)默認(rèn)值。
 */
 . = ALIGN(8);
 .text :
 {
  *(.__image_copy_start) --------------------------------------------------- (4)
/*
 *(4)__image_copy_start和__image_copy_end用于定義需要重定向的段,
 *    u-boot是一個(gè)分為重定向前初始化和重定向后初始化的bootloader,
 *    所以此處會(huì)定義在完成重定向前初始化后需要搬運(yùn)到ddr中數(shù)據(jù)的起始地址和結(jié)束地址;
 *
 *    大多數(shù)時(shí)候u-boot是運(yùn)行在受限的sram或者只讀的flash上,
 *    u-boot為了啟動(dòng)流程統(tǒng)一會(huì)在ddr未初始化和重定位之前不去訪問(wèn)全局變量,
 *    但是又為了保證u-boot能夠正常讀寫(xiě)全局變量,內(nèi)存,調(diào)用各類(lèi)驅(qū)動(dòng)能力,
 *    所以u(píng)-boot將啟動(dòng)初始化分為了兩個(gè)部分,重定向前初始化board_f和
 *    重定向后初始化  board_r,在重定向之前完成一些必要初始化,
 *    包括可能的ddr初始化,然后通過(guò)__image_copy_start和__image_copy_end
 *    將u-boot搬運(yùn)到ddr中,并在ddr中進(jìn)行重定向后初始化,這個(gè)時(shí)候的u-boot就可以
 *    正常訪問(wèn)全局變量等信息了。
 * 
 *    如果想要在board_f過(guò)程中讀寫(xiě)一些全局變量信息該怎么辦呢?
 *    u-boot通過(guò)定義global_data(gd)來(lái)完成此功能,
 *    后續(xù)在分析到時(shí)會(huì)詳細(xì)講解實(shí)現(xiàn)方式。
 */
  CPUDIR/start.o (.text*) -------------------------------------------------- (5)
/*
 *(5)定義了鏈接程序的頭部文本段,armv8就是
 *    arch/arm/cpu/armv8/start.S, 
 *    start.S中所有文本段將會(huì)鏈接到此段中并且段入口符號(hào)就是_start;
 */
 }

 /* This needs to come before *(.text*) */
 .efi_runtime : { ------------------------------------------------------------ (6)
/*
 *(6)在定義了efi運(yùn)行時(shí)相關(guān)支持時(shí)才會(huì)出現(xiàn)使用的段,一般不用關(guān)心;
 */
        __efi_runtime_start = .;
  *(.text.efi_runtime*)
  *(.rodata.efi_runtime*)
  *(.data.efi_runtime*)
        __efi_runtime_stop = .;
 }

 .text_rest : ---------------------------------------------------------------- (7)
/*
 *(7)除了start.o,其他的所有文本段將會(huì)鏈接到此段中;
 */
 {
  *(.text*)
 }

#ifdef CONFIG_ARMV8_PSCI -------------------------------------------------------- (8)
/*
 *(8)同(2),是PSCI相關(guān)功能的支持,一般不會(huì)使用;
 */
 .__secure_start :
#ifndef CONFIG_ARMV8_SECURE_BASE
  ALIGN(CONSTANT(COMMONPAGESIZE))
#endif
 {
  KEEP(*(.__secure_start))
 }

#ifndef CONFIG_ARMV8_SECURE_BASE
#define CONFIG_ARMV8_SECURE_BASE
#define __ARMV8_PSCI_STACK_IN_RAM
#endif
 .secure_text CONFIG_ARMV8_SECURE_BASE :
  AT(ADDR(.__secure_start) + SIZEOF(.__secure_start))
 {
  *(._secure.text)
  . = ALIGN(8);
  __secure_svc_tbl_start = .;
  KEEP(*(._secure_svc_tbl_entries))
  __secure_svc_tbl_end = .;
 }

 .secure_data : AT(LOADADDR(.secure_text) + SIZEOF(.secure_text))
 {
  *(._secure.data)
 }

 .secure_stack ALIGN(ADDR(.secure_data) + SIZEOF(.secure_data),
       CONSTANT(COMMONPAGESIZE)) (NOLOAD) :
#ifdef __ARMV8_PSCI_STACK_IN_RAM
  AT(ADDR(.secure_stack))
#else
  AT(LOADADDR(.secure_data) + SIZEOF(.secure_data))
#endif
 {
  KEEP(*(.__secure_stack_start))

  . = . + CONFIG_ARMV8_PSCI_NR_CPUS * ARM_PSCI_STACK_SIZE;

  . = ALIGN(CONSTANT(COMMONPAGESIZE));

  KEEP(*(.__secure_stack_end))
 }

#ifndef __ARMV8_PSCI_STACK_IN_RAM
 . = LOADADDR(.secure_stack);
#endif

 .__secure_end : AT(ADDR(.__secure_end)) {
  KEEP(*(.__secure_end))
  LONG(0x1d1071c); /* Must output something to reset LMA */
 }
#endif

 . = ALIGN(8);
 .rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } ------------------- (9)
/*
 *(9)所有僅讀數(shù)據(jù)將會(huì)在這個(gè)段中對(duì)齊排序存放好;
 */

 . = ALIGN(8);
 .data : { -------------------------------------------------------------------- (10)
/*
 *(10)所有數(shù)據(jù)段將會(huì)鏈接到此段中;
 */
  *(.data*)
 }

 . = ALIGN(8);

 . = .;

 . = ALIGN(8);
 .u_boot_list : { ------------------------------------------------------------- (11)
/*
 *(11)u_boot_list段定義了系統(tǒng)中當(dāng)前支持的所有命令和設(shè)備驅(qū)動(dòng),此段把散落在各個(gè)文件中
 *     通過(guò)U_BOOT_CMD的一系列拓展宏定義的命令和U_BOOT_DRIVER的拓展宏定義的設(shè)備驅(qū)動(dòng)收集到一起,
 *     并按照名字排序存放,以便后續(xù)在命令行快速檢索到命令并執(zhí)行和檢測(cè)注冊(cè)的設(shè)備和設(shè)備樹(shù)匹配
 *     probe設(shè)備驅(qū)動(dòng)初始化;(設(shè)備驅(qū)動(dòng)的probe只在定義了dm模塊化驅(qū)動(dòng)時(shí)有效)
 */
  KEEP(*(SORT(.u_boot_list*)));
 }

 . = ALIGN(8);

 .efi_runtime_rel : {
                __efi_runtime_rel_start = .;
  *(.rel*.efi_runtime)
  *(.rel*.efi_runtime.*)
                __efi_runtime_rel_stop = .;
 }

 . = ALIGN(8);

 .image_copy_end :
 {
  *(.__image_copy_end)
 }

 . = ALIGN(8);

 .rel_dyn_start : -------------------------------------------------------- (12)
/*
 *(12)一般u-boot運(yùn)行時(shí)是根據(jù)定義的基地址開(kāi)始執(zhí)行,如果加載地址和鏈接地址
 *     不一致則會(huì)出現(xiàn)不能執(zhí)行u-boot的問(wèn)題。通過(guò)一個(gè)
 *     配置CONFIG_POSITION_INDEPENDENT即可打開(kāi)地址無(wú)關(guān)功能,
 *     此選項(xiàng)會(huì)在鏈接u-boot時(shí)添加-PIE參數(shù)。此參數(shù)會(huì)在u-boot ELF文件中
 *     生成rela*段,u-boot通過(guò)讀取此段中表的相對(duì)地址值與實(shí)際運(yùn)行時(shí)地址值
 *     依次遍歷進(jìn)行修復(fù)當(dāng)前所有需要重定向地址,使其可以實(shí)現(xiàn)地址無(wú)關(guān)運(yùn)行;
 *     即無(wú)論鏈接基地址如何定義,u-boot也可以在任意ram地址
 *     運(yùn)行(一般需要滿(mǎn)足最低4K或者64K地址對(duì)齊);
 * 
 *     注意此功能只能在sram上實(shí)現(xiàn),因?yàn)榇斯δ軙?huì)在運(yùn)行時(shí)修改文本段數(shù)據(jù)段中的地址,
 *     如果此時(shí)運(yùn)行在片上flash,則不能寫(xiě)flash,導(dǎo)致功能失效無(wú)法實(shí)現(xiàn)地址無(wú)關(guān);
 */
 {
  *(.__rel_dyn_start)
 }

 .rela.dyn : {
  *(.rela*)
 }

 .rel_dyn_end :
 {
  *(.__rel_dyn_end)
 }

 _end = .;

 . = ALIGN(8);

 .bss_start : { -------------------------------------------------------- (13)
/*
 *(13)眾所周知的bbs段;
 */
  KEEP(*(.__bss_start));
 }

 .bss : {
  *(.bss*)
   . = ALIGN(8);
 }

 .bss_end : {
  KEEP(*(.__bss_end));
 }

 /DISCARD/ : { *(.dynsym) } -------------------------------------------- (14)
/*
 *(14)一些在鏈接時(shí)無(wú)用需要丟棄的段;
 */
 /DISCARD/ : { *(.dynstr*) }
 /DISCARD/ : { *(.dynamic*) }
 /DISCARD/ : { *(.plt*) }
 /DISCARD/ : { *(.interp*) }
 /DISCARD/ : { *(.gnu*) }

#ifdef CONFIG_LINUX_KERNEL_IMAGE_HEADER ----------------------------------- (15)
/*
 *(15)在efi加載時(shí)會(huì)很有用,主要在u-boot的二進(jìn)制頭部添加了一些頭部信息,
 *     包括大小端,數(shù)據(jù)段文本段大小等,以便于efi相關(guān)的加載器讀取信息,
 *     此頭部信息來(lái)自于Linux arm64的Image的頭部信息;該頭部也不屬于u-boot的
 *     一部分只是被附加上去的;
 */
#include "linux-kernel-image-header-vars.h"
#endif
}

4.2 u-boot-spl.lds

此鏈接腳本是標(biāo)準(zhǔn)的spl鏈接腳本,還包含了u_boot_list段,如果對(duì)應(yīng)自己board不需要命令行或者模塊化驅(qū)動(dòng)設(shè)備,只作為一個(gè)加載器則可以自定義更簡(jiǎn)略的鏈接腳本。

/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * (C) Copyright 2013
 * David Feng < fenghua@phytium.com.cn >
 *
 * (C) Copyright 2002
 * Gary Jennejohn, DENX Software Engineering, < garyj@denx.de >
 *
 * (C) Copyright 2010
 * Texas Instruments, < www.ti.com >
 * Aneesh V < aneesh@ti.com >
 */

MEMORY { .sram : ORIGIN = IMAGE_TEXT_BASE, ---------------------------------------- (1)
/*
 *(1) >XXX 的形式可以將指定段放入XXX規(guī)定的內(nèi)存中;一般u-boot-spl只有
 *    很小的可運(yùn)行內(nèi)存塊,所以spl中會(huì)舍去大量不需要用的段只保留關(guān)鍵的
 *    文本段數(shù)據(jù)段等,并且通過(guò) >.sram的形式將不在ddr初始化前用到的段定義到sdram中,
 *    后續(xù)只需在完成ddr初始化后將這些段搬運(yùn)到ddr中即可,而不需要額外的
 *    地址修復(fù)邏輯,如下:有一個(gè)sram 0x18000-0x19000,
 *    一個(gè)sdram 0x80000000 - 0x90000000,
 *    那么通過(guò) >.sram方式則map文件可能如下:
 *       0x18000 stext
 *       ...
 *       0x18100 sdata
 *       ...
 *       0x80000000 sbss
 *       ...
 */
  LENGTH = IMAGE_MAX_SIZE }
MEMORY { .sdram : ORIGIN = CONFIG_SPL_BSS_START_ADDR,
  LENGTH = CONFIG_SPL_BSS_MAX_SIZE }

OUTPUT_FORMAT("elf64-littleaarch64", "elf64-littleaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start) -------------------------------------------------------------------- (2)
/*
 *(2)同u-boot.lds一致,共用一套邏輯入口_start;
 */
SECTIONS
{
 .text : {
  . = ALIGN(8);
  *(.__image_copy_start) -------------------------------------------------- (3)
/*
 *(3)同樣的,如果spl需要重定向則會(huì)使用此段定義,大多數(shù)情況下spl中會(huì)用上重定向;
 */
  CPUDIR/start.o (.text*)
  *(.text*)
 } >.sram

 .rodata : {
  . = ALIGN(8);
  *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))
 } >.sram

 .data : {
  . = ALIGN(8);
  *(.data*)
 } >.sram

#ifdef CONFIG_SPL_RECOVER_DATA_SECTION ---------------------------------------- (4)
/*
 *(4)SPL_RECOVER_DATA_SECTION段用于保存數(shù)據(jù)段數(shù)據(jù),
 *    一些board在初始化時(shí)修改data段數(shù)據(jù),并在后續(xù)某個(gè)階段
 *    從此段中恢復(fù)data的原始數(shù)據(jù);
 */
 .data_save : {
  *(.__data_save_start)
  . = SIZEOF(.data);
  *(.__data_save_end)
 } >.sram
#endif

 .u_boot_list : {
  . = ALIGN(8);
  KEEP(*(SORT(.u_boot_list*)));
 } >.sram

 .image_copy_end : {
  . = ALIGN(8);
  *(.__image_copy_end)
 } >.sram

 .end : {
  . = ALIGN(8);
  *(.__end)
 } >.sram

 _image_binary_end = .;

 .bss_start (NOLOAD) : {
  . = ALIGN(8);
  KEEP(*(.__bss_start));
 } >.sdram -------------------------------------------------------------- (5)
/*
 *(5)將bss段數(shù)據(jù)定義到 >.sdram中,即可在初始化ddr后直接對(duì)此段地址清零
 *    即可使用全局未初始化變量,并且不會(huì)帶來(lái)副作用。
 */

 .bss (NOLOAD) : {
  *(.bss*)
   . = ALIGN(8);
 } >.sdram

 .bss_end (NOLOAD) : {
  KEEP(*(.__bss_end));
 } >.sdram

 /DISCARD/ : { *(.rela*) }
 /DISCARD/ : { *(.dynsym) }
 /DISCARD/ : { *(.dynstr*) }
 /DISCARD/ : { *(.dynamic*) }
 /DISCARD/ : { *(.plt*) }
 /DISCARD/ : { *(.interp*) }
 /DISCARD/ : { *(.gnu*) }
}

從上述的鏈接腳本可以看出,armv8的u-boot的啟動(dòng)是從arch/arm/cpu/armv8/start.S中的_start開(kāi)始的 ,并在后續(xù)初始化中調(diào)用了很多鏈接腳本中定義的地址符號(hào)表。

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • ARM
    ARM
    +關(guān)注

    關(guān)注

    134

    文章

    9312

    瀏覽量

    375163
  • Uboot
    +關(guān)注

    關(guān)注

    4

    文章

    128

    瀏覽量

    28895
  • ARMv8
    +關(guān)注

    關(guān)注

    1

    文章

    35

    瀏覽量

    14413
收藏 人收藏

    評(píng)論

    相關(guān)推薦
    熱點(diǎn)推薦

    U-boot的基本介紹

    從本文開(kāi)始,將陸續(xù)推送“手把手教你移植U-boot”系列文章,目標(biāo)是由淺入深地講解U-boot的工作流程、原理、配置方法和移植方法,手把手教你完成U-boot的移植工作,默認(rèn)硬件開(kāi)發(fā)平臺(tái)為ARM,操作系統(tǒng)為L(zhǎng)inux。
    發(fā)表于 07-14 16:52 ?3527次閱讀
    <b class='flag-5'>U-boot</b>的基本介紹

    U-boot的執(zhí)行流程

    本文主要講述了U-boot的執(zhí)行流程。
    發(fā)表于 07-14 16:58 ?900次閱讀
    <b class='flag-5'>U-boot</b>的執(zhí)行流程

    我的U-Boot鏈接腳本筆記

    以下是我學(xué)習(xí)u-boot鏈接腳本時(shí)做的筆記,歡迎指正錯(cuò)誤。/* 指明輸出的可執(zhí)行文件格式為elf,即小端模式的32位ARM指令 */OUTPUT_FORMAT("
    發(fā)表于 07-22 10:04

    SDK下使用make u-boot編譯NXP官方下載的u-boot編譯不成功怎么辦?

    .imgMKIMAGE u-boot-dtb.imgCATu-boot-dtb.binCOPY u-boot.binLDu-boot.elfCCspl/common/spl/spl.oCCspl/arch/arm/cpu/armv8
    發(fā)表于 12-31 06:24

    基于armv8架構(gòu)對(duì)u-boot進(jìn)行啟動(dòng)流程分析(一)

    ,比如mkimage的實(shí)現(xiàn)代碼在此處;4 u-boot armv8鏈接腳本在進(jìn)行源碼分析之前,首先看看u-boot
    發(fā)表于 05-23 15:59

    基于armv8架構(gòu)對(duì)u-boot進(jìn)行啟動(dòng)流程分析(二)

    為全局可見(jiàn)并在鏈接腳本中被聲明為入口地址,表示u-boot的入口地址為_(kāi)start;(2)首先進(jìn)入入口后有兩種可配置情況,一種就是定義了LINUX_KERNEL_IMAGE_HEADER
    發(fā)表于 05-23 16:05

    Porting U-Boot to the Control

    In this paper, the way of porting U-Boot to Control Computer Based MPC8349 will beintroduced
    發(fā)表于 01-25 15:45 ?13次下載

    u-boot的Makefile分析

    u-boot的Makefile分析 U-BOOT是一個(gè)LINUX下的工程,在編譯之前必須已經(jīng)安裝對(duì)應(yīng)體系結(jié)構(gòu)的交叉編譯環(huán)境,這里只針對(duì)ARM,編譯器系列軟件為arm-linux-*。 U-BOOT的下載
    發(fā)表于 05-17 09:16 ?2132次閱讀

    u-boot簡(jiǎn)介

    U-Boot,全稱(chēng) Universal Boot Loader,是遵循GPL條款的開(kāi)放源碼項(xiàng)目。U-Boot的作用是系統(tǒng)引導(dǎo)。U-Boot從FADSROM、
    發(fā)表于 10-14 11:17 ?3697次閱讀

    u-boot中Hush shell的功能及編寫(xiě)腳本的方法介紹

    了解u-boot中Hush shell的功能,以及如何為其編寫(xiě)腳本。 說(shuō)明了存儲(chǔ)和檢索腳本的方法。
    的頭像 發(fā)表于 11-22 07:00 ?6026次閱讀

    U-Boot架構(gòu)淺析

    導(dǎo)讀:嵌入式Linux系統(tǒng)搭建,bootloader是必不可少的一環(huán),而U-Boot已成嵌入式Linux事實(shí)標(biāo)準(zhǔn)。所以較為深入的分析U-Boot的設(shè)計(jì),對(duì)于更...
    發(fā)表于 02-07 11:56 ?7次下載
    <b class='flag-5'>U-Boot</b>架構(gòu)淺析

    Linux U-Boot開(kāi)發(fā)指南

    介紹 U-Boot 的編譯打包、基本配置、常用命令的使用、基本調(diào)試方法等, 為 U-BOOT 的移植及應(yīng)用開(kāi)發(fā)提供了基礎(chǔ)。
    的頭像 發(fā)表于 03-06 10:28 ?1668次閱讀
    Linux <b class='flag-5'>U-Boot</b>開(kāi)發(fā)指南

    基于armv8架構(gòu)來(lái)對(duì)u-boot進(jìn)行啟動(dòng)流程分析

    首先引用wiki上的簡(jiǎn)介:u-boot 是一個(gè)主要用于嵌入式系統(tǒng)的引導(dǎo)加載程序,可以支持多種不同的計(jì)算機(jī)系統(tǒng)結(jié)構(gòu)。
    發(fā)表于 06-09 09:39 ?1207次閱讀
    基于<b class='flag-5'>armv8</b>架構(gòu)來(lái)對(duì)<b class='flag-5'>u-boot</b>進(jìn)行啟動(dòng)流程分析

    armv8 u-boot的啟動(dòng)介紹

    先看arm官網(wǎng)提供的一張圖: 上圖詳細(xì)概括了arm官方推薦的armv8的啟動(dòng)層次結(jié)構(gòu): 官方將啟動(dòng)分為了BL1,BL2,BL31,BL32,BL33階段,根據(jù)順序,芯片啟動(dòng)后首先執(zhí)行BL1階段代碼
    的頭像 發(fā)表于 12-07 11:09 ?3170次閱讀
    <b class='flag-5'>armv8</b> <b class='flag-5'>u-boot</b>的啟動(dòng)介紹

    u-boot在匯編啟動(dòng)階段的相關(guān)操作介紹

    boot參數(shù), 進(jìn)行地址無(wú)關(guān)fixed,系統(tǒng)寄存器復(fù)位,底層平臺(tái)相關(guān)初始化等 ,啟動(dòng)代碼位于arch/arm/cpu/armv8/start.S, 入口地址為_(kāi)start。 啟動(dòng)前為后續(xù)流程做的一些平臺(tái)
    的頭像 發(fā)表于 12-07 11:22 ?883次閱讀

    電子發(fā)燒友

    中國(guó)電子工程師最喜歡的網(wǎng)站

    • 2931785位工程師會(huì)員交流學(xué)習(xí)
    • 獲取您個(gè)性化的科技前沿技術(shù)信息
    • 參加活動(dòng)獲取豐厚的禮品