鴻蒙內核源碼分析(ELF格式篇) | 應用程序入口并不是main

閱讀之前的說明

先說明,本篇很長,也很枯燥,若不是絕對的技術偏執狂是看不下去的.將通過一段簡單代碼去跟蹤編譯成ELF格式后的內容.看看ELF究竟長了怎樣的一副花花腸子,用readelf命令去窺視ELF的全貌,最后用objdump命令反匯編ELF.找到了大家熟悉main函數.
開始之前先說結論:

  • ELF 分四塊,其中三塊是描述信息(也叫頭信息),另一塊是內容,放的是所有段/區的內容.
    1. ELF頭定義全局性信息
    1. Segment(段)頭,內容描述段的名字,開始位置,類型,偏移,大小及每段由哪些區組成.
    1. 內容區,ELF有兩個重要概念?Segment(段) 和?Section(區),段比區大,二者之間關系如下:
    • 每個Segment可以包含多個Section
    • 每個Section可以屬于多個Segment
    • Segment之間可以有重合的部分
    • 拿大家熟知的.text.data.bss舉例,它們都叫區,但它們又屬于LOAD段.
    1. Section(區)頭,內容描述區的名字,開始位置,類型,偏移,大小等信息
  • ELF一體兩面,面對不同的場景扮演不同的角色,這是理解ELF的關鍵,鏈接器只關注1,3(區),4 的內容,加載器只關注1,2,3(段)的內容
  • 鴻蒙對EFL的定義在?kernel\extended\dynload\include\los_ld_elf_pri.h文件中

示例代碼

在windows目錄E:\harmony\docker\case_code_100下創建 main.c文件,如下:

#include <stdio.h>
void say_hello(char *who)
{printf("hello, %s!\n", who);
}
char *my_name = "harmony os";int main()
{say_hello(my_name);return 0;
}    

做好了環境映射,所以文件會同時出現在docker中.編譯生成ELF->運行->readelf -h查看app頭部信息.

root@5e3abe332c5a:/home/docker/case_code_100# ls
main.c
root@5e3abe332c5a:/home/docker/case_code_100# gcc -o app main.c
root@5e3abe332c5a:/home/docker/case_code_100# ls
app  main.c
root@5e3abe332c5a:/home/docker/case_code_100# ./app
hello, harmony os!

名正才言順

一下是關于ELF的所有中英名詞對照.建議先仔細看一篇再看系列篇部分.

可執行可連接格式 : ELF(Executable and Linking Format)
ELF文件頭:ELF header
基地址:base address
動態連接器: dynamic linker
動態連接: dynamic linking
全局偏移量表: got(global offset table)
進程鏈接表: plt(Procedure Linkage Table) 
哈希表: hash table
初始化函數 : initialization function
連接編輯器 : link editor
目標文件 : object file
函數連接表 : procedure linkage table
程序頭: program header
程序頭表 : program header table
程序解析器 : program interpreter
重定位: relocation
共享目標 : shared object
區(節): section
區(節)頭 : section header
區(節)表: section header table
段 : segment
字符串表 : string table
符號表: symbol table
終止函數 : termination function

ELF歷史

  • ELF(Executable and Linking Format),即"可執行可連接格式",最初由UNIX系統實驗室(UNIX System Laboratories – USL)做為應用程序二進制接口(Application Binary Interface - ABI)的一部分而制定和發布.是鴻蒙的主要可執行文件格式.

  • ELF的最大特點在于它有比較廣泛的適用性,通用的二進制接口定義使之可以平滑地移植到多種不同的操作環境上.這樣,不需要為每一種操作系統都定義一套不同的接口,因此減少了軟件的重復編碼與編譯,加強了軟件的可移植性.

ELF整體布局

ELF規范中把ELF文件寬泛地稱為"目標文件 (object file)",這與我們平時的理解不同.一般地,我們把經過編譯但沒有連接的文件(比如Unix/Linux上的.o文件)稱為目標文件,而ELF文件僅指連接好的可執行文件;在ELF規范中,所有符合ELF格式規范的都稱為ELF文件,也稱為目標文件,這兩個名字是相同的,而經過編譯但沒有連接的文件則稱為"可重定位文件 (relocatable file)“或"待重定位文件 (relocatable file)”.本文采用與此規范相同的命名方式,所以當提到可重定位文件時,一般可以理解為慣常所說的目標文件;而提到目標文件時,即指各種類型的ELF文件.

ELF格式可以表達四種類型的二進制對象文件(object files):

  • 可重定位文件(relocatable file),用于與其它目標文件進行連接以構建可執行文件或動態鏈接庫.可重定位文件就是常說的目標文件,由源文件編譯而成,但還沒有連接成可執行文件.在UNIX系統下,一般有擴展名".o".之所以稱其為"可重定位",是因為在這些文件中,如果引用到其它目標文件或庫文件中定義的符號(變量或者函數)的話,只是給出一個名字,這里還并不知道這個符號在哪里,其具體的地址是什么.需要在連接的過程中,把對這些外部符號的引用重新定位到其真正定義的位置上,所以稱目標文件為"可重定位"或者"待重定位"的.
  • 可執行文件(executable file)包含代碼和數據,是可以直接運行的程序.其代碼和數據都有固定的地址 (或相對于基地址的偏移 ),系統可根據這些地址信息把程序加載到內存執行.
  • 共享目標文件(shared object file),即動態連接庫文件.它在以下兩種情況下被使用:第一,在連接過程中與其它動態鏈接庫或可重定位文件一起構建新的目標文件;第二,在可執行文件被加載的過程中,被動態鏈接到新的進程中,成為運行代碼的一部分.包含了代碼和數據,這些數據是在鏈接時被鏈接器(ld)和運行時動態鏈接器(ld.so.l、libc.so.l、ld-linux.so.l)使用的.
  • 核心轉儲文件(core dump file,就是core dump文件)
可重定位文件用在編譯和鏈接階段.
可執行文件用在程序運行階段.
共享庫則同時用在編譯鏈接和運行階段,本篇 app 就是個 DYN,可直接運行.Type:                              DYN (Shared object file)

在不同階段,我們可以用不同視角來理解ELF文件,整體布局如下圖所示:

從上圖可見,ELF格式文件整體可分為四大部分:

  • ELF Header: 在文件的開始,描述整個文件的組織.即readelf -h app看到的內容
  • Program Header Table: 告訴系統如何創建進程映像.用來構造進程映像的目標文件必須具有程序頭部表,可重定位文件可以不需要這個表.表描述所有段(Segment)信息,即readelf -l app看到的前半部分內容.
  • Segments:段(Segment)由若干區(Section)組成.是從加載器角度來描述?ELF?文件.加載器只關心?ELF header,?Program header table?和?Segment?這三部分內容。 在加載階段可以忽略 section header table 來處理程序(所以很多加固手段刪除了section header table
  • Sections: 是從鏈接器角度來描述?ELF?文件. 鏈接器只關心?ELF headerSections?以及?Section header table?這三部分內容。在鏈接階段,可以忽略?program header table?來處理文件.
  • Section Header Table:描述區(Section)信息的數組,每個元素對應一個區,通常包含在可重定位文件中,可執行文件中為可選(通常包含) 即readelf -S app看到的內容
  • 從圖中可以看出?Segment:Section(M:N)是多對多的包含關系.Segment是由多個Section組成,Section也能屬于多個段.

ELF頭信息

ELF頭部信息對應鴻蒙源碼結構體為?LDElf32Ehdr, 各字段含義已一一注解,很容易理解.

//kernel\extended\dynload\include\los_ld_elf_pri.h
/* Elf header */
#define LD_EI_NIDENT           16
typedef struct {UINT8       elfIdent[LD_EI_NIDENT]; /* Magic number and other info *///含前16個字節,又可細分成class、data、version等字段,具體含義不用太關心,只需知道前4個字節點包含`ELF`關鍵字,這樣可以判斷當前文件是否是ELF格式UINT16      elfType;                /* Object file type *///表示具體ELF類型,可重定位文件/可執行文件/共享庫文件UINT16      elfMachine;             /* Architecture *///表示cpu架構UINT32      elfVersion;             /* Object file version *///表示文件版本號UINT32      elfEntry;               /* Entry point virtual address *///對應`Entry point address`,程序入口函數地址,通過進程虛擬地址空間地址表達UINT32      elfPhoff;               /* Program header table file offset *///對應`Start of program headers`,表示program header table在文件內的偏移位置UINT32      elfShoff;               /* Section header table file offset *///對應`Start of section headers`,表示section header table在文件內的偏移位置UINT32      elfFlags;               /* Processor-specific flags *///表示與CPU處理器架構相關的信息UINT16      elfHeadSize;            /* ELF header size in bytes *///對應`Size of this header`,表示本ELF header自身的長度UINT16      elfPhEntSize;           /* Program header table entry size *///對應`Size of program headers`,表示program header table中每個元素的大小UINT16      elfPhNum;               /* Program header table entry count *///對應`Number of program headers`,表示program header table中元素個數UINT16      elfShEntSize;           /* Section header table entry size *///對應`Size of section headers`,表示section header table中每個元素的大小UINT16      elfShNum;               /* Section header table entry count *///對應`Number of section headers`,表示section header table中元素的個數UINT16      elfShStrIndex;          /* Section header string table index *///對應`Section header string table index`,表示描述各section字符名稱的string table在section header table中的下標
} LDElf32Ehdr;
root@5e3abe332c5a:/home/docker/case_code_100# readelf -h app
ELF Header:Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00Class:                             ELF64Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              DYN (Shared object file)Machine:                           Advanced Micro Devices X86-64Version:                           0x1Entry point address:               0x1060Start of program headers:          64 (bytes into file)Start of section headers:          14784 (bytes into file)Flags:                             0x0Size of this header:               64 (bytes)Size of program headers:           56 (bytes)Number of program headers:         13Size of section headers:           64 (bytes)Number of section headers:         31Section header string table index: 30

解讀

顯示的信息,就是 ELF header 中描述的所有內容了。這個內容與結構體?LDElf32Ehdr?中的成員變量是一一對應的!
Size of this header: 64 (bytes)也就是說:ELF header 部分的內容,一共是 64 個字節。64個字節碼長啥樣可以用命令od -Ax -t x1 -N 64 app看,并對照結構體LDElf32Ehdr來理解.

root@5e3abe332c5a:/home/docker/case_code_100/51# od -Ax -t x1 -N 64 app
000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
000010 03 00 3e 00 01 00 00 00 60 10 00 00 00 00 00 00
000020 40 00 00 00 00 00 00 00 c0 39 00 00 00 00 00 00
000030 00 00 00 00 40 00 38 00 0d 00 40 00 1f 00 1e 00
000040

簡單解釋一下命令的幾個選項:

-Ax: 顯示地址的時候,用十六進制來表示。如果使用 -Ad,意思就是用十進制來顯示地址;
-t -x1: 顯示字節碼內容的時候,使用十六進制(x),每次顯示一個字節(1);
-N 64:只需要讀取64個字節;

這里留意這幾個內容,下面會說明,先記住.

Entry point address:               0x1060   //代碼區 .text 起始位置,即程序運行開始位置
Size of program headers:           56 (bytes)//每個段頭大小
Number of program headers:         13       //段數量
Size of section headers:           64 (bytes)//每個區頭大小
Number of section headers:         31       //區數量
Section header string table index: 30       //字符串數組索引,該區記錄所有區名稱

段(Segment)頭信息

段(Segment)信息對應鴻蒙源碼結構體為?LDElf32Phdr

//kernel\extended\dynload\include\los_ld_elf_pri.h
/* Program Header */
typedef struct {UINT32 type;     /* Segment type */	//段類型UINT32 offset;   /* Segment file offset */		//此數據成員給出本段內容在文件中的位置,即段內容的開始位置相對于文件開頭的偏移量.UINT32 vAddr;    /* Segment virtual address */	//此數據成員給出本段內容的開始位置在進程空間中的虛擬地址.UINT32 phyAddr;  /* Segment physical address */	//此數據成員給出本段內容的開始位置在進程空間中的物理地址.對于目前大多數現代操作系統而言,應用程序中段的物理地址事先是不可知的,所以目前這個成員多數情況下保留不用,或者被操作系統改作它用.UINT32 fileSize; /* Segment size in file */		//此數據成員給出本段內容在文件中的大小,單位是字節,可以是0.UINT32 memSize;  /* Segment size in memory */	//此數據成員給出本段內容在內容鏡像中的大小,單位是字節,可以是0.UINT32 flags;    /* Segment flags */			//此數據成員給出了本段內容的屬性.UINT32 align;    /* Segment alignment */		//對于可裝載的段來說,其p_vaddr和p_offset的值至少要向內存頁面大小對齊.
} LDElf32Phdr;

解讀
readelf -l查看app段頭部表內容,先看命令返回的前半部分:

root@5e3abe332c5a:/home/docker/case_code_100# readelf -l app 
Elf file type is DYN (Shared object file)
Entry point 0x1060
There are 13 program headers, starting at offset 64
Program Headers:Type           Offset             VirtAddr           PhysAddrFileSiz            MemSiz              Flags  AlignPHDR           0x0000000000000040 0x0000000000000040 0x00000000000000400x00000000000002d8 0x00000000000002d8  R      0x8INTERP         0x0000000000000318 0x0000000000000318 0x00000000000003180x000000000000001c 0x000000000000001c  R      0x1[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]LOAD           0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000618 0x0000000000000618  R      0x1000LOAD           0x0000000000001000 0x0000000000001000 0x00000000000010000x0000000000000225 0x0000000000000225  R E    0x1000LOAD           0x0000000000002000 0x0000000000002000 0x00000000000020000x0000000000000190 0x0000000000000190  R      0x1000LOAD           0x0000000000002db8 0x0000000000003db8 0x0000000000003db80x0000000000000260 0x0000000000000268  RW     0x1000DYNAMIC        0x0000000000002dc8 0x0000000000003dc8 0x0000000000003dc80x00000000000001f0 0x00000000000001f0  RW     0x8NOTE           0x0000000000000338 0x0000000000000338 0x00000000000003380x0000000000000020 0x0000000000000020  R      0x8NOTE           0x0000000000000358 0x0000000000000358 0x00000000000003580x0000000000000044 0x0000000000000044  R      0x4GNU_PROPERTY   0x0000000000000338 0x0000000000000338 0x00000000000003380x0000000000000020 0x0000000000000020  R      0x8GNU_EH_FRAME   0x000000000000201c 0x000000000000201c 0x000000000000201c0x000000000000004c 0x000000000000004c  R      0x4GNU_STACK      0x0000000000000000 0x0000000000000000 0x00000000000000000x0000000000000000 0x0000000000000000  RW     0x10GNU_RELRO      0x0000000000002db8 0x0000000000003db8 0x0000000000003db80x0000000000000248 0x0000000000000248  R      0x1

數一下一共13個段,其實在ELF頭信息也告訴了我們共13個段

Size of program headers:           56 (bytes)//每個段頭大小
Number of program headers:         13       //段數量

仔細看下這些段的開始地址和大小,發現有些段是重疊的.那是因為一個區可以被多個段所擁有.例如:0x2db8?對應的?.init_array區就被第四LOAD?和?GNU_RELRO兩段所共有.

PHDR,此類型header元素描述了program header table自身的信息.從這里的內容看出,示例程序的program header table在文件中的偏移(Offset)為0x40,即64號字節處.該段映射到進程空間的虛擬地址(VirtAddr)為0x40.PhysAddr暫時不用,其保持和VirtAddr一致.該段占用的文件大小FileSiz0x2d8.運行時占用進程空間內存大小MemSiz也為0x2d8.Flags標記表示該段的讀寫權限,這里R表示只讀,Align對齊為8,表明本段按8字節對齊.

INTERP,此類型header元素描述了一個特殊內存段,該段內存記錄了動態加載解析器的訪問路徑字符串.示例程序中,該段內存位于文件偏移0x318處,即緊跟program header table.映射的進程虛擬地址空間地址為0x318.文件長度和內存映射長度均為0x1c,即28個字符,具體內容為/lib64/ld-linux-x86-64.so.2.段屬性為只讀,并按字節對齊.

LOAD,此類型header元素描述了可加載到進程空間的代碼區或數據區:

  • 其第二段包含了代碼區,文件內偏移為0x1000,文件大小為0x225,映射到進程地址0x001000處,屬性為只讀可執行(RE),段地址按0x1000(4K)邊界對齊.
  • 其第四段包含了數據區,文件內偏移為0x2db8,文件大小為0x260,映射到進程地址0x003db8處,屬性為可讀可寫(RW),段地址也按0x1000(4K)邊界對齊.

DYNAMIC,此類型header元素描述了動態加載段,其內部通常包含了一個名為.dynamic的動態加載區.這也是一個數組,每個元素描述了與動態加載相關的各方面信息,將在系列篇(動態加載篇)中介紹.該段是從文件偏移0x2dc8處開始,長度為0x1f0,并映射到進程的0x3dc8.可見該段和上一個段LOAD4 0x2db8是有重疊的.

GNU_STACK,可執行棧,即棧區,在加載段的過程中,當發現存在PT_GNU_STACK,也就是GNU_STACK segment 的存在,如果存在這個這個段的話,看這個段的 flags 是否有可執行權限,來設置對應的值.必須為RW方式.

再看命令返回內容的后半部分-段區映射關系

 Section to Segment mapping:Segment Sections...0001     .interp02     .interp .note.gnu.property .note.gnu.build-id .note.ABI-tag .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt03     .init .plt .plt.got .plt.sec .text .fini04     .rodata .eh_frame_hdr .eh_frame05     .init_array .fini_array .dynamic .got .data .bss06     .dynamic07     .note.gnu.property08     .note.gnu.build-id .note.ABI-tag09     .note.gnu.property10     .eh_frame_hdr1112     .init_array .fini_array .dynamic .got

13個段和31個區的映射關系,右邊其實不止31個區,是因為一個區可以共屬于多個段,例如?.dynamic?,.interp.got
Segment:Section(M:N)是多對多的包含關系.Segment是由多個Section組成,Section也能屬于多個段.這個很重要,說第二遍了.

  • INTERP段只包含了.interp
  • LOAD2段包含.interp.plt.text等區,.text代碼區位于這個段. 這個段是 'RE’屬性,只讀可執行的.
  • LOAD4包含.dynamic.data.bss等區, 數據區位于這個段.這個段是 'RW’屬性,可讀可寫.?.data.bss都是數據區,有何區別呢?
  • .data(ZI data)它用來存放初始化了的(initailized)全局變量(global)和初始化了的靜態變量(static).
  • .bss(RW data )它用來存放未初始化的(uninitailized)全局變量(global)和未初始化的靜態變量.
  • DYNAMIC段包含.dynamic區.

區表

區(section)頭表信息對應鴻蒙源碼結構體為?LDElf32Shdr

//kernel\extended\dynload\include\los_ld_elf_pri.h
/* Section header */
typedef struct {UINT32 shName;      /* Section name (string tbl index) *///表示每個區的名字UINT32 shType;      /* Section type *///表示每個區的功能UINT32 shFlags;     /* Section flags *///表示每個區的屬性UINT32 shAddr;      /* Section virtual addr at execution *///表示每個區的進程映射地址UINT32 shOffset;    /* Section file offset *///表示文件內偏移UINT32 shSize;      /* Section size in bytes *///表示區的大小UINT32 shLink;      /* Link to another section *///Link和Info記錄不同類型區的相關信息UINT32 shInfo;      /* Additional section information *///Link和Info記錄不同類型區的相關信息UINT32 shAddrAlign; /* Section alignment *///表示區的對齊單位UINT32 shEntSize;   /* Entry size if section holds table *///表示區中每個元素的大小(如果該區為一個數組的話,否則該值為0)
} LDElf32Shdr;

示例程序共生成31個區.其實在頭文件中也已經告訴我們了

Size of section headers:           64 (bytes)//每個區頭大小
Number of section headers:         31       //區數量

通過readelf -S命令看看示例程序中 section header table的內容,如下所示.

root@5e3abe332c5a:/home/docker/case_code_100# readelf -S app
There are 31 section headers, starting at offset 0x39c0:Section Headers:[Nr] Name              Type             Address           OffsetSize              EntSize          Flags  Link  Info  Align[ 0]                   NULL             0000000000000000  000000000000000000000000  0000000000000000           0     0     0[ 1] .interp           PROGBITS         0000000000000318  00000318000000000000001c  0000000000000000   A       0     0     1[ 2] .note.gnu.propert NOTE             0000000000000338  000003380000000000000020  0000000000000000   A       0     0     8[ 3] .note.gnu.build-i NOTE             0000000000000358  000003580000000000000024  0000000000000000   A       0     0     4[ 4] .note.ABI-tag     NOTE             000000000000037c  0000037c0000000000000020  0000000000000000   A       0     0     4[ 5] .gnu.hash         GNU_HASH         00000000000003a0  000003a00000000000000024  0000000000000000   A       6     0     8[ 6] .dynsym           DYNSYM           00000000000003c8  000003c800000000000000a8  0000000000000018   A       7     1     8[ 7] .dynstr           STRTAB           0000000000000470  000004700000000000000084  0000000000000000   A       0     0     1[ 8] .gnu.version      VERSYM           00000000000004f4  000004f4000000000000000e  0000000000000002   A       6     0     2[ 9] .gnu.version_r    VERNEED          0000000000000508  000005080000000000000020  0000000000000000   A       7     1     8[10] .rela.dyn         RELA             0000000000000528  0000052800000000000000d8  0000000000000018   A       6     0     8[11] .rela.plt         RELA             0000000000000600  000006000000000000000018  0000000000000018  AI       6    24     8[12] .init             PROGBITS         0000000000001000  00001000000000000000001b  0000000000000000  AX       0     0     4[13] .plt              PROGBITS         0000000000001020  000010200000000000000020  0000000000000010  AX       0     0     16[14] .plt.got          PROGBITS         0000000000001040  000010400000000000000010  0000000000000010  AX       0     0     16[15] .plt.sec          PROGBITS         0000000000001050  000010500000000000000010  0000000000000010  AX       0     0     16[16] .text             PROGBITS         0000000000001060  0000106000000000000001b5  0000000000000000  AX       0     0     16[17] .fini             PROGBITS         0000000000001218  00001218000000000000000d  0000000000000000  AX       0     0     4[18] .rodata           PROGBITS         0000000000002000  00002000000000000000001b  0000000000000000   A       0     0     4[19] .eh_frame_hdr     PROGBITS         000000000000201c  0000201c000000000000004c  0000000000000000   A       0     0     4[20] .eh_frame         PROGBITS         0000000000002068  000020680000000000000128  0000000000000000   A       0     0     8[21] .init_array       INIT_ARRAY       0000000000003db8  00002db80000000000000008  0000000000000008  WA       0     0     8[22] .fini_array       FINI_ARRAY       0000000000003dc0  00002dc00000000000000008  0000000000000008  WA       0     0     8[23] .dynamic          DYNAMIC          0000000000003dc8  00002dc800000000000001f0  0000000000000010  WA       7     0     8[24] .got              PROGBITS         0000000000003fb8  00002fb80000000000000048  0000000000000008  WA       0     0     8[25] .data             PROGBITS         0000000000004000  000030000000000000000018  0000000000000000  WA       0     0     8[26] .bss              NOBITS           0000000000004018  000030180000000000000008  0000000000000000  WA       0     0     1[27] .comment          PROGBITS         0000000000000000  00003018000000000000002a  0000000000000001  MS       0     0     1[28] .symtab           SYMTAB           0000000000000000  000030480000000000000648  0000000000000018          29    46     8[29] .strtab           STRTAB           0000000000000000  000036900000000000000216  0000000000000000           0     0     1[30] .shstrtab         STRTAB           0000000000000000  000038a6000000000000011a  0000000000000000           0     0     1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), I (info),L (link order), O (extra OS processing required), G (group), T (TLS),C (compressed), x (unknown), o (OS specific), E (exclude),l (large), p (processor specific)

String Table

在 ELF header 的最后 2 個字節是 0x1e 0x00,即30. 它對應結構體中的成員?elfShStrIndex,意思是這個 ELF 文件中,字符串表是一個普通的 Section,在這個 Section 中,存儲了 ELF 文件中使用到的所有的字符串。
我們使用readelf -x讀出下標30區的數據:

root@5e3abe332c5a:/home/docker/case_code_100# readelf -x 30 app Hex dump of section '.shstrtab':0x00000000 002e7379 6d746162 002e7374 72746162 ..symtab..strtab0x00000010 002e7368 73747274 6162002e 696e7465 ..shstrtab..inte0x00000020 7270002e 6e6f7465 2e676e75 2e70726f rp..note.gnu.pro0x00000030 70657274 79002e6e 6f74652e 676e752e perty..note.gnu.0x00000040 6275696c 642d6964 002e6e6f 74652e41 build-id..note.A0x00000050 42492d74 6167002e 676e752e 68617368 BI-tag..gnu.hash0x00000060 002e6479 6e73796d 002e6479 6e737472 ..dynsym..dynstr0x00000070 002e676e 752e7665 7273696f 6e002e67 ..gnu.version..g0x00000080 6e752e76 65727369 6f6e5f72 002e7265 nu.version_r..re0x00000090 6c612e64 796e002e 72656c61 2e706c74 la.dyn..rela.plt0x000000a0 002e696e 6974002e 706c742e 676f7400 ..init..plt.got.0x000000b0 2e706c74 2e736563 002e7465 7874002e .plt.sec..text..0x000000c0 66696e69 002e726f 64617461 002e6568 fini..rodata..eh0x000000d0 5f667261 6d655f68 6472002e 65685f66 _frame_hdr..eh_f0x000000e0 72616d65 002e696e 69745f61 72726179 rame..init_array0x000000f0 002e6669 6e695f61 72726179 002e6479 ..fini_array..dy0x00000100 6e616d69 63002e64 61746100 2e627373 namic..data..bss0x00000110 002e636f 6d6d656e 7400              ..comment.

可以發現,這里其實是一堆字符串,這些字符串對應的就是各個區的名字.因此section header table中每個元素的Name字段其實是這個string table的索引.為節省空間而做的設計,再回頭看看ELF header中的?elfShStrIndex

Section header string table index: 30 //字符串數組索引,該區記錄所有區名稱

它的值正好就是30,指向了當前的string table.

符號表 Symbol Table

Section Header Table中,還有一類SYMTAB(DYNSYM)區,該區叫符號表.符號表中的每個元素對應一個符號,記錄了每個符號對應的實際數值信息,通常用在重定位過程中或問題定位過程中,進程執行階段并不加載符號表.符號表對應鴻蒙源碼結構體為?LDElf32Sym.
//kernel\extended\dynload\include\los_ld_elf_pri.h

/* Symbol table */
typedef struct {UINT32 stName;  /* Symbol table name (string tbl index) *///表示符號對應的源碼字符串,為對應String Table中的索引UINT32 stValue; /* Symbol table value *///表示符號對應的數值UINT32 stSize;  /* Symbol table size *///表示符號對應數值的空間占用大小UINT8 stInfo;   /* Symbol table type and binding *///表示符號的相關信息 如符號類型(變量符號、函數符號)UINT8 stOther;  /* Symbol table visibility */UINT16 stShndx; /* Section table index *///表示與該符號相關的區的索引,例如函數符號與對應的代碼區相關
} LDElf32Sym;

readelf -s讀出示例程序中的符號表,如下所示

root@5e3abe332c5a:/home/docker/case_code_100# readelf -s appSymbol table '.dynsym' contains 7 entries:Num:    Value          Size Type    Bind   Vis      Ndx Name0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.2.5 (2)3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.2.5 (2)4: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__5: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable6: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (2)Symbol table '.symtab' contains 67 entries:Num:    Value          Size Type    Bind   Vis      Ndx Name0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND1: 0000000000000318     0 SECTION LOCAL  DEFAULT    12: 0000000000000338     0 SECTION LOCAL  DEFAULT    23: 0000000000000358     0 SECTION LOCAL  DEFAULT    34: 000000000000037c     0 SECTION LOCAL  DEFAULT    45: 00000000000003a0     0 SECTION LOCAL  DEFAULT    56: 00000000000003c8     0 SECTION LOCAL  DEFAULT    67: 0000000000000470     0 SECTION LOCAL  DEFAULT    78: 00000000000004f4     0 SECTION LOCAL  DEFAULT    89: 0000000000000508     0 SECTION LOCAL  DEFAULT    910: 0000000000000528     0 SECTION LOCAL  DEFAULT   1011: 0000000000000600     0 SECTION LOCAL  DEFAULT   1112: 0000000000001000     0 SECTION LOCAL  DEFAULT   1213: 0000000000001020     0 SECTION LOCAL  DEFAULT   1314: 0000000000001040     0 SECTION LOCAL  DEFAULT   1415: 0000000000001050     0 SECTION LOCAL  DEFAULT   1516: 0000000000001060     0 SECTION LOCAL  DEFAULT   1617: 0000000000001218     0 SECTION LOCAL  DEFAULT   1718: 0000000000002000     0 SECTION LOCAL  DEFAULT   1819: 000000000000201c     0 SECTION LOCAL  DEFAULT   1920: 0000000000002068     0 SECTION LOCAL  DEFAULT   2021: 0000000000003db8     0 SECTION LOCAL  DEFAULT   2122: 0000000000003dc0     0 SECTION LOCAL  DEFAULT   2223: 0000000000003dc8     0 SECTION LOCAL  DEFAULT   2324: 0000000000003fb8     0 SECTION LOCAL  DEFAULT   2425: 0000000000004000     0 SECTION LOCAL  DEFAULT   2526: 0000000000004018     0 SECTION LOCAL  DEFAULT   2627: 0000000000000000     0 SECTION LOCAL  DEFAULT   2728: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c29: 0000000000001090     0 FUNC    LOCAL  DEFAULT   16 deregister_tm_clones30: 00000000000010c0     0 FUNC    LOCAL  DEFAULT   16 register_tm_clones31: 0000000000001100     0 FUNC    LOCAL  DEFAULT   16 __do_global_dtors_aux32: 0000000000004018     1 OBJECT  LOCAL  DEFAULT   26 completed.806033: 0000000000003dc0     0 OBJECT  LOCAL  DEFAULT   22 __do_global_dtors_aux_fin34: 0000000000001140     0 FUNC    LOCAL  DEFAULT   16 frame_dummy35: 0000000000003db8     0 OBJECT  LOCAL  DEFAULT   21 __frame_dummy_init_array_36: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS main.c37: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c38: 000000000000218c     0 OBJECT  LOCAL  DEFAULT   20 __FRAME_END__39: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS40: 0000000000003dc0     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_end41: 0000000000003dc8     0 OBJECT  LOCAL  DEFAULT   23 _DYNAMIC42: 0000000000003db8     0 NOTYPE  LOCAL  DEFAULT   21 __init_array_start43: 000000000000201c     0 NOTYPE  LOCAL  DEFAULT   19 __GNU_EH_FRAME_HDR44: 0000000000003fb8     0 OBJECT  LOCAL  DEFAULT   24 _GLOBAL_OFFSET_TABLE_45: 0000000000001000     0 FUNC    LOCAL  DEFAULT   12 _init46: 0000000000001210     5 FUNC    GLOBAL DEFAULT   16 __libc_csu_fini47: 0000000000004010     8 OBJECT  GLOBAL DEFAULT   25 my_name48: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab49: 0000000000004000     0 NOTYPE  WEAK   DEFAULT   25 data_start50: 0000000000004018     0 NOTYPE  GLOBAL DEFAULT   25 _edata51: 0000000000001218     0 FUNC    GLOBAL HIDDEN    17 _fini52: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.2.553: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_54: 0000000000004000     0 NOTYPE  GLOBAL DEFAULT   25 __data_start55: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__56: 0000000000004008     0 OBJECT  GLOBAL HIDDEN    25 __dso_handle57: 0000000000002000     4 OBJECT  GLOBAL DEFAULT   18 _IO_stdin_used58: 00000000000011a0   101 FUNC    GLOBAL DEFAULT   16 __libc_csu_init59: 0000000000004020     0 NOTYPE  GLOBAL DEFAULT   26 _end60: 0000000000001060    47 FUNC    GLOBAL DEFAULT   16 _start61: 0000000000004018     0 NOTYPE  GLOBAL DEFAULT   26 __bss_start62: 0000000000001174    30 FUNC    GLOBAL DEFAULT   16 main63: 0000000000001149    43 FUNC    GLOBAL DEFAULT   16 say_hello64: 0000000000004018     0 OBJECT  GLOBAL HIDDEN    25 __TMC_END__65: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable66: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@@GLIBC_2.2

在最后位置找到了親切的老朋友?mainsay_hello

    62: 0000000000001174    30 FUNC    GLOBAL DEFAULT   16 main63: 0000000000001149    43 FUNC    GLOBAL DEFAULT   16 say_hello

main函數符號對應的數值為0x1174,其類型為FUNC,大小為30字節,對應的代碼區索引為16.
say_hello函數符號對應數值為0x1149,其類型為FUNC,大小為43字節,對應的代碼區索引同為16.
Section Header Table:

  [16] .text             PROGBITS         0000000000001060  0000106000000000000001b5  0000000000000000  AX       0     0     16

反匯編代碼區

在理解了String TableSymbol Table的作用后,通過objdump反匯編來理解一下.text代碼區:

root@5e3abe332c5a:/home/docker/case_code_100# objdump -j .text -l -C -S app0000000000001149 <say_hello>:
say_hello():1149:       f3 0f 1e fa             endbr64114d:       55                      push   %rbp114e:       48 89 e5                mov    %rsp,%rbp1151:       48 83 ec 10             sub    $0x10,%rsp1155:       48 89 7d f8             mov    %rdi,-0x8(%rbp)1159:       48 8b 45 f8             mov    -0x8(%rbp),%rax115d:       48 89 c6                mov    %rax,%rsi1160:       48 8d 3d 9d 0e 00 00    lea    0xe9d(%rip),%rdi        # 2004 <_IO_stdin_used+0x4>1167:       b8 00 00 00 00          mov    $0x0,%eax116c:       e8 df fe ff ff          callq  1050 <printf@plt>1171:       90                      nop1172:       c9                      leaveq1173:       c3                      retq0000000000001174 <main>:
main():1174:       f3 0f 1e fa             endbr641178:       55                      push   %rbp1179:       48 89 e5                mov    %rsp,%rbp117c:       48 8b 05 8d 2e 00 00    mov    0x2e8d(%rip),%rax        # 4010 <my_name>1183:       48 89 c7                mov    %rax,%rdi1186:       e8 be ff ff ff          callq  1149 <say_hello>118b:       b8 00 00 00 00          mov    $0x0,%eax1190:       5d                      pop    %rbp1191:       c3                      retq1192:       66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1)1199:       00 00 00119c:       0f 1f 40 00             nopl   0x0(%rax)

0x1149?0x1174正是say_hellomain函數的入口地址.并看到了激動人心的指令

1186:       e8 be ff ff ff          callq  1149 <say_hello>

很佩服你還能看到這里,牛逼,牛逼! 看了這么久還記得開頭的C代碼的樣子嗎? 再看一遍 : )

#include <stdio.h>
void say_hello(char *who)
{printf("hello, %s!\n", who);
}
char *my_name = "harmony os";
int main()
{say_hello(my_name);return 0;
}
root@5e3abe332c5a:/home/docker/case_code_100# ./app
hello, harmony os!    

但是!!! 暈,怎么還有but,西卡西…,上面請大家記住的還有一個地方沒說到

Entry point address:               0x1060   //代碼區 .text 起始位置,即程序運行開始位置

它的地址并不是main函數位置0x1174,是0x1060!而且代碼區的開始位置是0x1060沒錯的.

  [16] .text             PROGBITS         0000000000001060  0000106000000000000001b5  0000000000000000  AX       0     0     16

難度main不是入口地址? 那0x1060上放的是何方神圣,再查符號表發現是

    60: 0000000000001060    47 FUNC    GLOBAL DEFAULT   16 _start

從反匯編堆中找到?_start

0000000000001060 <_start>:
_start():1060:       f3 0f 1e fa             endbr641064:       31 ed                   xor    %ebp,%ebp1066:       49 89 d1                mov    %rdx,%r91069:       5e                      pop    %rsi106a:       48 89 e2                mov    %rsp,%rdx106d:       48 83 e4 f0             and    $0xfffffffffffffff0,%rsp1071:       50                      push   %rax1072:       54                      push   %rsp1073:       4c 8d 05 96 01 00 00    lea    0x196(%rip),%r8        # 1210 <__libc_csu_fini>107a:       48 8d 0d 1f 01 00 00    lea    0x11f(%rip),%rcx        # 11a0 <__libc_csu_init>1081:       48 8d 3d ec 00 00 00    lea    0xec(%rip),%rdi        # 1174 <main>1088:       ff 15 52 2f 00 00       callq  *0x2f52(%rip)        # 3fe0 <__libc_start_main@GLIBC_2.2.5>108e:       f4                      hlt108f:       90                      nop

這才看到了0x1174main函數.所以真正的說法是:

  • 從內核動態加載的視角看,程序運行首個函數并不是main,而是_start.
  • 但從應用程序開發者視角看,main就是啟動函數.

鴻蒙全棧開發全新學習指南

也為了積極培養鴻蒙生態人才,讓大家都能學習到鴻蒙開發最新的技術,針對一些在職人員、0基礎小白、應屆生/計算機專業、鴻蒙愛好者等人群,整理了一套純血版鴻蒙(HarmonyOS Next)全棧開發技術的學習路線【包含了大廠APP實戰項目開發】

本路線共分為四個階段:

第一階段:鴻蒙初中級開發必備技能

第二階段:鴻蒙南北雙向高工技能基礎:gitee.com/MNxiaona/733GH

第三階段:應用開發中高級就業技術

第四階段:全網首發-工業級南向設備開發就業技術:https://gitee.com/MNxiaona/733GH

《鴻蒙 (Harmony OS)開發學習手冊》(共計892頁)

如何快速入門?

1.基本概念
2.構建第一個ArkTS應用
3.……

開發基礎知識:gitee.com/MNxiaona/733GH

1.應用基礎知識
2.配置文件
3.應用數據管理
4.應用安全管理
5.應用隱私保護
6.三方應用調用管控機制
7.資源分類與訪問
8.學習ArkTS語言
9.……

基于ArkTS 開發

1.Ability開發
2.UI開發
3.公共事件與通知
4.窗口管理
5.媒體
6.安全
7.網絡與鏈接
8.電話服務
9.數據管理
10.后臺任務(Background Task)管理
11.設備管理
12.設備使用信息統計
13.DFX
14.國際化開發
15.折疊屏系列
16.……

鴻蒙開發面試真題(含參考答案):gitee.com/MNxiaona/733GH

鴻蒙入門教學視頻:

美團APP實戰開發教學:gitee.com/MNxiaona/733GH

寫在最后

  • 如果你覺得這篇內容對你還蠻有幫助,我想邀請你幫我三個小忙:
  • 點贊,轉發,有你們的 『點贊和評論』,才是我創造的動力。
  • 關注小編,同時可以期待后續文章ing🚀,不定期分享原創知識。
  • 想要獲取更多完整鴻蒙最新學習資源,請移步前往小編:gitee.com/MNxiaona/733GH

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/11856.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/11856.shtml
英文地址,請注明出處:http://en.pswp.cn/web/11856.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Image to Music V2 :只需上傳一張照片,自動轉換成與圖片內容匹配的音頻!

前言 我們之前肯定已經見過了很多文本生成圖片、文本生成聲音以及AI翻唱歌曲 等多種AI產品&#xff08;模型&#xff09;。 其實音樂和圖片從某種意義上來說都是藝術創作的一種形式&#xff0c;它們可以相互配合&#xff0c;共同呈現出一種更加豐富、感性的表達方式。 將圖片…

弘君資本:人形機器人概念走強,盛通股份漲停,怡合達、鼎智科技等拉升

人形機器人概念14日盤中拉升走高&#xff0c;到發稿&#xff0c;盛通股份漲停&#xff0c;怡合達、鼎智科技漲約6%&#xff0c;索辰科技、偉創電氣、豐立智能等漲超4%。 音訊面上&#xff0c;5月13日&#xff0c;宇樹發布人形智能體Unitree G1&#xff0c;身高127cm,體重35kg&…

[240514] OpenAI 發布 GPT-4o,人機交互的歷史性時刻 | 蘋果芯片進軍服務器劍指AI? | 谷歌大會以AI為主

目錄 OpenAI 發布 GPT-4o&#xff0c;人機交互的歷史時刻蘋果芯片進軍服務器&#xff0c;劍指生成式 AI2024年谷歌開發者大會將圍繞 AI 展開 OpenAI 發布 GPT-4o&#xff0c;人機交互的歷史時刻 OpenAI 發布了 GPT-4o&#xff0c;大家一直都想要現在終于等到的語音助手 : 勿需…

618值得入手的數碼產品怎么選?2024 買過不后悔的數碼好物分享

在數字時代的浪潮中&#xff0c;每一次的購物狂歡節都如同一場科技盛宴&#xff0c;讓我們有機會接觸到最前沿、最實用的數碼產品&#xff0c;而“618”無疑是這場盛宴中最為引人矚目的日子之一。面對琳瑯滿目的商品&#xff0c;如何選擇那些真正值得入手的數碼好物&#xff0c…

易寶OA-ExecuteQueryForDataSetBinary處sql注入

免責聲明&#xff1a; 本文內容為學習筆記分享&#xff0c;僅供技術學習參考&#xff0c;請勿用作違法用途&#xff0c;任何個人和組織利用此文所提供的信息而造成的直接或間接后果和損失&#xff0c;均由使用者本人負責&#xff0c;與作者無關&#xff01;&#xff01;&#…

Centos 安裝jenkins 多分支流水線部署前后端項目

1、安裝jenkins 1.1 安裝jdk 要求&#xff1a;11及以上版本 yum install yum install java-11-openjdk 1.2 安裝jenkins 導入鏡像 sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo出現以下錯誤 執行以下命令 sudo yum …

前端使用原生JS怎么上傳本地路徑的文件到后端【附源碼】

本文不使用<input type"file">等前端上傳組件 一、為什么不能使用本地文件路徑上傳&#xff1f; 前端不能直接根據本地文件路徑&#xff08;例如 C:\Users\Username\Documents\image.jpg&#xff09;上傳文件到后端服務器&#xff0c;原因主要在于瀏覽器的安全…

使用java遠程提交flink任務到yarn集群

使用java遠程提交flink任務到yarn集群 背景 由于業務需要&#xff0c;使用命令行的方式提交flink任務比較麻煩&#xff0c;要么將后端任務部署到大數據集群&#xff0c;要么弄一個提交機&#xff0c;感覺都不是很離線。經過一些調研&#xff0c;發現可以實現遠程的任務發布。…

LOTO示波器軟件PC緩存(波形錄制與回放)功能

當打開PC緩存功能后, 軟件將采用先進先出的原則排隊對示波器采集的每一幀數據, 進行幀緩存。 當發現屏幕中有感興趣的波形掠過時, 鼠標點擊軟件的(暫停)按鈕, 可以選擇回看某一幀的波形。一幀數據的量 是 當前用戶選擇時基檔位緩沖區總數據大小。不同時基檔位緩沖區大小不同&am…

談談std::map的lower_bound

我們知道std::map內部是一個紅黑樹&#xff0c;放到std::map里的數據等有一個能比較大小的方法。它相當于java里面的TreeMap。 它里面有個lower_bound方法&#xff0c;返回一個迭代器&#xff0c;它指向map里第一個大于等于參數的元素。 方法的簽名很簡單&#xff0c;但是在不同…

富格林:有效預防黑幕阻撓被騙

富格林指出&#xff0c;在投資領域&#xff0c;現貨黃金是一種備受推崇的貴金屬投資品種。倘若能有效預防黑幕阻撓被騙的情況&#xff0c;事實上現貨黃金是很多投資者的“理想型”。然而要想有效地預防黑幕阻撓被騙&#xff0c;就需要掌握足夠多的投資技巧。為此&#xff0c;富…

Milvus 基本概念

Milvus 是一個開源的向量數據庫&#xff0c;專門用于高效地存儲、管理和檢索大規模向量數據。它基于 Apache 許可證 2.0 版本發布&#xff0c;由 Zilliz 公司開源并維護。 Milvus 的設計理念是為了解決向量數據存儲和檢索的挑戰。在許多應用中&#xff0c;向量數據是一種重要的…

強化學習——馬爾可夫過程的理解

目錄 一、馬爾可夫過程1.隨機過程2.馬爾可夫性質3.馬爾可夫過程4.馬爾可夫過程示例 參考文獻 一、馬爾可夫過程 1.隨機過程 隨機過程是概率論的“動態”版本。普通概率論研究的是固定不變的隨機現象&#xff0c;而隨機過程則專注于那些隨時間不斷變化的情況&#xff0c;比如天…

C# 使用channel 實現Plc 異步任務之間的通信

channel 通信的例子: using ConsoleApp2; using System.Collections.Concurrent; using System.Threading.Channels;var queue = new BlockingCollection<Message>(new ConcurrentQueue<Message>());var opt = new BoundedChannelOptions(10) {FullMode = BoundedC…

Linux環境快速部署mysql5.7

1 網絡下載rpm包 wget -c https://repo.huaweicloud.com/mysql/Downloads/MySQL-5.7/mysql-5.7.37-1.el7.x86_64.rpm-bundle.tar2 解壓 tar xf mysql-5.7.37-1.el7.x86_64.rpm-bundle.tar3 數據庫之間會沖突因此需要卸載mariadb-libs yum remove mariadb-libs4 安裝 如果沒有…

R語言兩種方法實現隨機分層抽樣

為了減少數據分布的不平衡&#xff0c;提供高樣本的代表性&#xff0c;可將數據按特征分層一定的層次&#xff0c;在每個層次抽取一定量的樣本&#xff0c;為分層抽樣。分層抽樣的特點是將科學分組法與抽樣法結合在一起&#xff0c;分組減小了各抽樣層變異性的影響&#xff0c;…

HTTP協議及Python實現

最近的項目需要頻繁在前后端之間傳輸數據&#xff0c;本篇主要介紹HTTP協議以及數據傳輸方法。 1 HTTP協議 1.1 http協議簡介 HTTP(Hypertext Transfer Protocol)是一種用于傳輸超文本數據的應用層協議。它是萬維網上數據交換的基礎&#xff0c;定義了客戶端和服務器之間進行通…

C語言指針詳解(三)

目錄 前言 一. 回調函數是什么&#xff1f; 1.定義 2. 代碼示例&#xff1a;計數器 2.1 使用回調函數改造前 2.2 使用回調函數改造后 二. qsort使用舉例 1. qsort介紹 2. 使用qsort函數排序整型數據 3. 使用qsort排序結構體數據 三. qsort函數的模擬實現 四. sizeo…

代碼隨想錄:螺旋矩陣II相關題目推薦(54、LCR146)

59.螺旋矩陣II 題目 給你一個正整數 n &#xff0c;生成一個包含 1 到 n2 所有元素&#xff0c;且元素按順時針順序螺旋排列的 n x n 正方形矩陣 matrix 。 示例 1&#xff1a; 輸入&#xff1a;n 3 輸出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 代碼&#xff08;新解法&am…

MyBatis——MyBatis 參數處理

一、單個簡單類型參數 簡單類型包括&#xff1a; byte short int long float double char Byte Short Integer Long Float Double Character String java.util.Date java.sql.Date parameterType 屬性&#xff1a;告訴 MyBatis 參數的類型 MyBatis 自帶類型自動推斷機制…