SlideShare ist ein Scribd-Unternehmen logo
1 von 32
Beginner's Guide to Linkers
Pinkus.Chang
Outline
• Question : compiler error
• Naming of Parts: What's in a C File
• Dissecting An Object File
• Adding C++ to the picture
• What’s the linker does part1
• What’s the linker does part2
• Adding C++ to the picture
Question : compiler error
A typical example of what triggers this explanation is when I help
someone who has a link error like:
Ans: ‘Almost certainly missing extern "C"'
Naming of Parts: What's in a C File
• Declaration vs Definition
• 不直觀的特殊情況
– 用 static 修飾的局部變量實際上是全局變量,因為雖然它們僅在某個函
數中可見,但其生命週期存在於整個程序中
– 同樣,用 static 修飾的全局變量也被認為是全局的,儘管它們只能由它
們所在的文件內的函數訪問
• Compiler
– 編譯器都只允許使用已經聲明(declaration)過的變量和函數,程序員對
編譯器的承諾:向它確保這個變量或函數已經在程序中的別處定義過了
• Linker
– 兌現這一承諾
– 編譯器會留個空白(blank),這個「空白」(我們也稱之為「引用」
(reference))擁有與之相關聯的一個名字,但該名字對應的值還尚未可
知。
新夥伴 c_parts.c
Code
Data
Global Local Dynamic
Initialized Uninitialized Initialized Uninitialized
Declaration
(extern)int
fn_a()
extern int z_global; N/A N/A N/A
Definition
static int fn_b()
int fn_c()
int x_global_init= 1;
(All scope)
static int y_global_init= 2;
(at file scope)
int x_global_unninit;
(All scope)
static int y_global_init= 2;
(at file scope)
int y_local_init= 1;
(at function scope)
int y_local_uninit;
(at function scope)
(int* p =
malloc(sizeof(int));)
要process起來才會有橘色部分
Dissecting An Object File : nm
U: 該類型表示未定義的引用(undefined reference),即我們前文所提及的「空白」
(blanks)。對於示例中的目標文件,共有兩個未定義類型:「fn_a」 和 「z_global」。(有
些 nm 的版本還可能包括 section(譯註:即宏彙編中的區,後文直接使用section而不另作中文
翻譯)的名字,section的內容通常為 *UND* 或 UNDEF)
t/T: 該類型指明了代碼定義的位置。t 和 T 用於區分該函數是定義在文件內部(t)還是定義
在文件外部(T)——例如,用於表明某函數是否聲明為 static。同樣的
,有些系統包括 section ,內容形如.text
d/D: 該類型表明當前變量是一個已初始化的變量,d 指明這是一個局部變量,D 則表示全局
變量。如果存在 section ,則內容形如 .data
b/B: 對於非初始化的變量,我們用 b 來表示該變量是靜態(static)或是局部的(local),否
則,用 B 或 C 來表示。這時 section 的內容可能為.bss 或者 *COM*
What The Linker Does : Part 1
增加新夥伴 main.c
Linker main.c and c_parts.c
main.c
c_parts.c
on a UNIX system, the linker is typically invoked with ld
nm main.exe
「未定義的引用」都已消失
多複雜的細節,看上去很混亂
=>劃線開頭的內容都過濾掉
Duplicate Symbols
• C++
– one definition rule
• 即鏈接階段,一個符號有且只能定義一次
• C
– 任何的函數或者已經初始化的全局變量,都有且只能有一次定義即鏈接階段,一個符號
有且只能定義一次
– But…未初始化的全局變量的定義可以看成是一種臨時性定義(a tentative definition)
• Another language
– Fortran : Linker 還得對付除 C/C++ 以外的其它語言
– one definition rule 並不適用
– As a result, it's actually quite common for UNIX linkers not to complain about duplicate
definitions of symbols—at least, not when the duplicate symbol is an uninitialized global
variable (this is sometimes known as the "relaxed ref/def model" of linking). If this worries you
(and it probably should), check the documentation for your compiler linker—there may well
be a --work-properly option that tightens up the behavior. For example, for the GNU toolchain
the -fno-common option to the compiler forces it to put uninitialized variables into the BSS
segment rather than generating these common blocks.
What The Operating System Does
1.把code segment(text segment) 從 disk 搬到 memory
------------------------------------------------------
2.把data segment (initial global variable )搬入
3.把bss segment( uninitialized global variable) 搬進去 但不用copy其值 預設都是0
------------------------------------------------------
Local variable 不用linker幫忙 因為他生命週期只在process運行時
1.Local variable stack
2.Malloc等等變數  heap
3.Heap stack 撞再一起,就是滿了
123
1 2
The size command didn't list a stack or heap segment for hello_world
or hello_world.o. Why do you think that is?
Review memory-layout
1.compiler and linker : gcc
2.Loader : exec
Disk file
Process memory
What The Linker Does: Part 2
Static libraries
1.Command : ar
2.libXXX.a
3.gcc –llibXXX.a
4.未解決符號表
it builds a list of the symbols it hasn't been able to resolve yet.
When all of the explicitly specified objects are done with, the linker now has
another place to look for the symbols that are left on this
5.Libraries的處理順序,相依性。
linker按命令行從左到右的順序進行處理,只有前一個庫處理結束了,才會繼續
處理下一個庫。換句話說,如果後一個庫中導入的目標文件依賴於前一個庫中的
某個符號,那麼鏈接器將無法進行自動關聯。
File a.o b.o libx.a liby.a
Object a.o b.o x1.o x2.ox3.o y1.o y2.o y3.o
Definitions
a1,
a2,
a3
b1,
b2
x11,
x12,
x13
x21,
x22,
x23
X31,
x32
y11,
y12
y21,
y22
y31, y32
Undefined
references
b2,
x12
a3,
y22
x23,
y12
y11 y21 x31
gcc main.exe –lx –ly a.o b.o
Step1: we can get b2 a3 ,but we still lose x12 y22
Step2: try to find x12 y22 in libx.a ,get x12 on x1.o
=> leak y22,x23,y12
Step3: try to find x23 and y12 because linker is still dealing with libx.a
=> leak y22 y12 y11
Setp4: try to find y21 y12 y11 on liby.a ,不過由於 y22 的存在,y2.o 無論如何都必須導入
=> finish
File a.o b.o libx.a liby.a
Object a.o b.o x1.o x2.ox3.o y1.o y2.o y3.o
Definitions
a1,
a2,
a3
b1,
b2
x11,
x12,
x13
x21,
x22,
x23
X31,
x32
y11,
y12
y21,
y22
y31, y32
Undefined
references
b2,
x12
a3,
y22
y32
x23,
y12
y11 y21 x31
Step5.y32 can find on liby.a , but ……….
1.x31 has been linked pervious time(libx.a) by this stage the linker has already
finished with libx.a and would not find the definition (in x3.o) for this symbo
Cyclic dependency
We don’t load x3.o previous time.
Shared libraries
• Static libraries 缺點
– 1.浪費空間,所有代碼都要copy同一份
– 2.修改某個lib bug後,要全部重新link,才可已拿到修正後的code
• Shared libraries
– IOU(我欠你)
• 等到process真正在運行的時候,才對這TAG處理
• 所以linker 發現 so 並不會把symbol包到exe 內,而是將symbol與其對應的libraries 記錄下來。
– Small linker: ld.so
• Before program counter come to int main()
• 負責檢查貼過標籤的內容,並完成鏈接的最後一個步驟:導入庫裡的代碼,並將所有符號都關
聯在一起
– the granularity of the link
• 如果程序中只引用了共享庫裡的某個符號(比如,只使用了 libc.so 庫中的 printf),那麼整個共
享庫都將映射到程序地址空間中,這與靜態庫的行為完全不同,靜態庫中只會導入與該符號相
關的那個目標文件。
Put another way….
• shared library
=>can resolve to references between objects in the same library getting resolved.
因為整個so 內含的.o都會被放入,不會直有用到才放的問題
File a.o b.o libx.so liby.so
Object a.o b.o x1.o x2.ox3.o y1.o y2.o y3.o
Definitions
a1,
a2,
a3
b1,
b2
x11,
x12,
x13
x21,
x22,
x23
X31,
x32
y11,
y12
y21,
y22
y31, y32
Undefined
references
b2,
x12
a3,
y22
y32
x23,
y12
y11 y21 x31
x3.o can be linked when ld.so working
Command ldd
• ldd
– shows the set of shared libraries that an executable (or a
shared library) depends on
– LD_LIBRARY_PATH
• Typically, the loader looks for libraries in the list of directories held
in the LD_LIBRARY_PATH environment variable.
Adding C++ To The Picture
1. Function Overloading & Name Mangling
2. Initialization of Statics
3. Templates
4. Dynamically Loaded Libraries
Function Overloading & Name Mangling
1.Good friends: nm
2.ii:integer ff: float dd:double
The mangled names : nm --demangle
need to add extern C
The C++ code is actually looking for something like "_Z7findmaxii" but only
finding "findmax"
By the way, note that an extern "C" linkage
declaration is ignored for member functions
Initialization of Statics
For this code, the (demangled) output of nm gives:
Class
1.Constructor
2.W:weak symbols
3.所有這些全局對象的構造函數的調用順序並未定
義——因此,這完全取決於鏈接器的實現
way1: the sane way
way2: the Sun way : 看不懂 GG, PASS~~
Templates
Way1: the sane way
對於第一種方法,即將每個實例函數代碼
展開,每個目標文件中都會包含它所調用
的所有模板函數的代碼,以上文的 C++ 文
件為例,目標文件內容如下:
目標函數將這兩個函數的定義標記成「弱
符號」(weak symbos),這表示當鏈接器
最終生成可執行程序時,將只留下所有重
複定義的其中之一,剩餘的定義都將棄之
不用
Dynamically Loaded Libraries
• 最終的鏈接操作可以延遲到程序真正運行的時
刻
• dlopen and dlsym
• dlopen兩種選擇
– 解決導入庫的所有未定義符號(RTLD_NOW): 長
– 按遇到的順序一個個解決未定義符號
(RTLD_LAZY): 快,看有可能在process運作中,發
現undefined symbol 導致process crash
Conclusion
• C++ compiler 還有很多東西看不懂
EX:動態載入與C++特性的交互
Interaction with C++ Features
• Shared libraries with GCC on Linux
– http://cprogramming.com/
– 這篇不錯,寫得很入門。
常見command
# All information
readelf -a
# check shared libraries
readelf –a *.so | grep Shared
# GCC version
readelf -p .comment
# CPU version
readelf -h
# module version
readelf -p .mmodule_version
# check symbol on execute file
nm
# check memory layout after linker
size
# Linking with a shared library
ld
# shows the set of shared libraries that an executable (or a
shared library) depends on
ldd
# generate static libraries
ar
# telling GCC where to find the shared library
gcc -L/home/username/foo -Wall -o test main.c -lfoo
'Note that the -lfoo option is not looking for foo.o, but libfoo.so'
# header
gcc -I
addr2line
# addr2line
PC: /lib/libc-2.24.so [0x74a65bac]
LR: /usr/lib/libadaptor.so.0.1.0 [0x759becd8]
754c8000-75a9a000 r-xp 00000000 b3:1f 146 /usr/lib/libkadaptor.so.0.1.0
75a9a000-75aaa000 ---p 005d2000 b3:1f 146 /usr/lib/libkadaptor.so.0.1.0
75aaa000-75b89000 rw-p 005d2000 b3:1f 146 /usr/lib/libkadaptor.so.0.1.0
>>> hex(0x759becd8-0x754c8000)
'0x4f6cd8'
$ addr2line -a -f -e ./libkadaptor.so 0x4f6cd8
Reference
• http://www.lurklurk.org/linkers/linkers.html
• http://blog.jobbole.com/96225/
• http://www.geeksforgeeks.org/memory-
layout-of-c-program/
• http://www.cprogramming.com/tutorial/shar
ed-libraries-linux-gcc.html
More Detail
• John Levine, Linkers and Loaders: contains lots and lots of information about the details of linkers
and loaders work, including all the things I've skipped here. There also appears to be an online
version of it (or an early draft of it) here
• Excellent link on the Mach-O format for binaries on Mac OS X [Added 27-Mar-06]
• Peter Van Der Linden, Expert C Programming: excellent book which includes more information
about how C codetransforms into a running program than any other C text I've encountered
• Scott Meyers, More Effective C++: Item 34 covers the pitfalls of combining C and C++ in the same
program (whether linker-related or not).
• Bjarne Stroustrup, The Design and Evolution of C++: section 11.3 discusses linkage in C++ and how
it came about
• Margaret A. Ellis & Bjarne Stroustrup, The Annotated C++ Reference Manual: section 7.2c describes
one particular name mangling scheme
• ELF format reference [PDF]
• Two interesting articles on creating tiny Linux executables and a minimal Hello World in particular.
• "How To Write Shared Libraries" [PDF] by Ulrich Drepper has more details on ELF and relocation.

Weitere ähnliche Inhalte

Was ist angesagt?

SECR'13 Lightweight linux shared libraries profiling
SECR'13 Lightweight linux shared libraries profilingSECR'13 Lightweight linux shared libraries profiling
SECR'13 Lightweight linux shared libraries profiling
OSLL
 
Binaries Are Not Only Output
Binaries Are Not Only OutputBinaries Are Not Only Output
Binaries Are Not Only Output
Hajime Morrita
 
Other Approaches (Concurrency)
Other Approaches (Concurrency)Other Approaches (Concurrency)
Other Approaches (Concurrency)
Sri Prasanna
 
Unit 3
Unit  3Unit  3
Unit 3
siddr
 

Was ist angesagt? (20)

ZFConf 2012: Dependency Management в PHP и Zend Framework 2 (Кирилл Чебунин)
ZFConf 2012: Dependency Management в PHP и Zend Framework 2 (Кирилл Чебунин)ZFConf 2012: Dependency Management в PHP и Zend Framework 2 (Кирилл Чебунин)
ZFConf 2012: Dependency Management в PHP и Zend Framework 2 (Кирилл Чебунин)
 
C++ process new
C++ process newC++ process new
C++ process new
 
Flex
FlexFlex
Flex
 
The Ring programming language version 1.3 book - Part 60 of 88
The Ring programming language version 1.3 book - Part 60 of 88The Ring programming language version 1.3 book - Part 60 of 88
The Ring programming language version 1.3 book - Part 60 of 88
 
Stop Monkeys Fall
Stop Monkeys FallStop Monkeys Fall
Stop Monkeys Fall
 
Unix processes
Unix processesUnix processes
Unix processes
 
A Life of breakpoint
A Life of breakpointA Life of breakpoint
A Life of breakpoint
 
Gradle in a Polyglot World
Gradle in a Polyglot WorldGradle in a Polyglot World
Gradle in a Polyglot World
 
C tutorial
C tutorialC tutorial
C tutorial
 
SECR'13 Lightweight linux shared libraries profiling
SECR'13 Lightweight linux shared libraries profilingSECR'13 Lightweight linux shared libraries profiling
SECR'13 Lightweight linux shared libraries profiling
 
Binaries Are Not Only Output
Binaries Are Not Only OutputBinaries Are Not Only Output
Binaries Are Not Only Output
 
Mod02 compilers
Mod02 compilersMod02 compilers
Mod02 compilers
 
Other Approaches (Concurrency)
Other Approaches (Concurrency)Other Approaches (Concurrency)
Other Approaches (Concurrency)
 
ZFConf 2012: Реализация доступа к СУБД IBM DB2 посредством встраиваемого SQL ...
ZFConf 2012: Реализация доступа к СУБД IBM DB2 посредством встраиваемого SQL ...ZFConf 2012: Реализация доступа к СУБД IBM DB2 посредством встраиваемого SQL ...
ZFConf 2012: Реализация доступа к СУБД IBM DB2 посредством встраиваемого SQL ...
 
Unix
UnixUnix
Unix
 
Unit 3
Unit  3Unit  3
Unit 3
 
Ruby meetup ROM
Ruby meetup ROMRuby meetup ROM
Ruby meetup ROM
 
SystemC Ports
SystemC PortsSystemC Ports
SystemC Ports
 
On Web Browsers
On Web BrowsersOn Web Browsers
On Web Browsers
 
Systemc overview 2010
Systemc overview 2010Systemc overview 2010
Systemc overview 2010
 

Ähnlich wie Beginner's guide to linkers

Using Git as your VCS with Bioconductor
Using Git as your VCS with BioconductorUsing Git as your VCS with Bioconductor
Using Git as your VCS with Bioconductor
timyates
 
In-depth look at the Flex compiler and HFCD
In-depth look at the Flex compiler and HFCDIn-depth look at the Flex compiler and HFCD
In-depth look at the Flex compiler and HFCD
Stop Coding
 

Ähnlich wie Beginner's guide to linkers (20)

Native hook mechanism in Android Bionic linker
Native hook mechanism in Android Bionic linkerNative hook mechanism in Android Bionic linker
Native hook mechanism in Android Bionic linker
 
OptView2 MUC meetup slides
OptView2 MUC meetup slidesOptView2 MUC meetup slides
OptView2 MUC meetup slides
 
HKG15-207: Advanced Toolchain Usage Part 3
HKG15-207: Advanced Toolchain Usage Part 3HKG15-207: Advanced Toolchain Usage Part 3
HKG15-207: Advanced Toolchain Usage Part 3
 
HKG15-211: Advanced Toolchain Usage Part 4
HKG15-211: Advanced Toolchain Usage Part 4HKG15-211: Advanced Toolchain Usage Part 4
HKG15-211: Advanced Toolchain Usage Part 4
 
tools.ppt
tools.ppttools.ppt
tools.ppt
 
LinuxCon Japan 2010 suzaki
LinuxCon Japan 2010 suzakiLinuxCon Japan 2010 suzaki
LinuxCon Japan 2010 suzaki
 
Using Git as your VCS with Bioconductor
Using Git as your VCS with BioconductorUsing Git as your VCS with Bioconductor
Using Git as your VCS with Bioconductor
 
Kernel Recipes 2016 - Would an ABI changes visualization tool be useful to Li...
Kernel Recipes 2016 - Would an ABI changes visualization tool be useful to Li...Kernel Recipes 2016 - Would an ABI changes visualization tool be useful to Li...
Kernel Recipes 2016 - Would an ABI changes visualization tool be useful to Li...
 
Exciting JavaScript - Part II
Exciting JavaScript - Part IIExciting JavaScript - Part II
Exciting JavaScript - Part II
 
The true story_of_hello_world
The true story_of_hello_worldThe true story_of_hello_world
The true story_of_hello_world
 
Whirlwind tour of the Runtime Dynamic Linker
Whirlwind tour of the Runtime Dynamic LinkerWhirlwind tour of the Runtime Dynamic Linker
Whirlwind tour of the Runtime Dynamic Linker
 
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
2013.02.02 지앤선 테크니컬 세미나 - Xcode를 활용한 디버깅 팁(OSXDEV)
 
Control Flow Analysis
Control Flow AnalysisControl Flow Analysis
Control Flow Analysis
 
C++ Boot Camp Part 2
C++ Boot Camp Part 2C++ Boot Camp Part 2
C++ Boot Camp Part 2
 
In-depth look at the Flex compiler and HFCD
In-depth look at the Flex compiler and HFCDIn-depth look at the Flex compiler and HFCD
In-depth look at the Flex compiler and HFCD
 
ECMAScript 6: A Better JavaScript for the Ambient Computing Era
ECMAScript 6: A Better JavaScript for the Ambient Computing EraECMAScript 6: A Better JavaScript for the Ambient Computing Era
ECMAScript 6: A Better JavaScript for the Ambient Computing Era
 
stackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Three
stackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Threestackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Three
stackconf 2022: Cluster Management: Heterogeneous, Lightweight, Safe. Pick Three
 
Ch3 gnu make
Ch3 gnu makeCh3 gnu make
Ch3 gnu make
 
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
The Hitchhiker's Guide to Faster Builds. Viktor Kirilov. CoreHard Spring 2019
 
Adding a BOLT pass
Adding a BOLT passAdding a BOLT pass
Adding a BOLT pass
 

Kürzlich hochgeladen

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ssuser89054b
 
"Lesotho Leaps Forward: A Chronicle of Transformative Developments"
"Lesotho Leaps Forward: A Chronicle of Transformative Developments""Lesotho Leaps Forward: A Chronicle of Transformative Developments"
"Lesotho Leaps Forward: A Chronicle of Transformative Developments"
mphochane1998
 
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak HamilCara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Kandungan 087776558899
 
Verification of thevenin's theorem for BEEE Lab (1).pptx
Verification of thevenin's theorem for BEEE Lab (1).pptxVerification of thevenin's theorem for BEEE Lab (1).pptx
Verification of thevenin's theorem for BEEE Lab (1).pptx
chumtiyababu
 
DeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakesDeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakes
MayuraD1
 

Kürzlich hochgeladen (20)

data_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdfdata_management_and _data_science_cheat_sheet.pdf
data_management_and _data_science_cheat_sheet.pdf
 
NO1 Top No1 Amil Baba In Azad Kashmir, Kashmir Black Magic Specialist Expert ...
NO1 Top No1 Amil Baba In Azad Kashmir, Kashmir Black Magic Specialist Expert ...NO1 Top No1 Amil Baba In Azad Kashmir, Kashmir Black Magic Specialist Expert ...
NO1 Top No1 Amil Baba In Azad Kashmir, Kashmir Black Magic Specialist Expert ...
 
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptxOrlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
Orlando’s Arnold Palmer Hospital Layout Strategy-1.pptx
 
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
 
"Lesotho Leaps Forward: A Chronicle of Transformative Developments"
"Lesotho Leaps Forward: A Chronicle of Transformative Developments""Lesotho Leaps Forward: A Chronicle of Transformative Developments"
"Lesotho Leaps Forward: A Chronicle of Transformative Developments"
 
Thermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - VThermal Engineering-R & A / C - unit - V
Thermal Engineering-R & A / C - unit - V
 
Engineering Drawing focus on projection of planes
Engineering Drawing focus on projection of planesEngineering Drawing focus on projection of planes
Engineering Drawing focus on projection of planes
 
HAND TOOLS USED AT ELECTRONICS WORK PRESENTED BY KOUSTAV SARKAR
HAND TOOLS USED AT ELECTRONICS WORK PRESENTED BY KOUSTAV SARKARHAND TOOLS USED AT ELECTRONICS WORK PRESENTED BY KOUSTAV SARKAR
HAND TOOLS USED AT ELECTRONICS WORK PRESENTED BY KOUSTAV SARKAR
 
Thermal Engineering Unit - I & II . ppt
Thermal Engineering  Unit - I & II . pptThermal Engineering  Unit - I & II . ppt
Thermal Engineering Unit - I & II . ppt
 
Wadi Rum luxhotel lodge Analysis case study.pptx
Wadi Rum luxhotel lodge Analysis case study.pptxWadi Rum luxhotel lodge Analysis case study.pptx
Wadi Rum luxhotel lodge Analysis case study.pptx
 
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak HamilCara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
Cara Menggugurkan Sperma Yang Masuk Rahim Biyar Tidak Hamil
 
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptxA CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
A CASE STUDY ON CERAMIC INDUSTRY OF BANGLADESH.pptx
 
Double Revolving field theory-how the rotor develops torque
Double Revolving field theory-how the rotor develops torqueDouble Revolving field theory-how the rotor develops torque
Double Revolving field theory-how the rotor develops torque
 
Introduction to Serverless with AWS Lambda
Introduction to Serverless with AWS LambdaIntroduction to Serverless with AWS Lambda
Introduction to Serverless with AWS Lambda
 
DC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equationDC MACHINE-Motoring and generation, Armature circuit equation
DC MACHINE-Motoring and generation, Armature circuit equation
 
Tamil Call Girls Bhayandar WhatsApp +91-9930687706, Best Service
Tamil Call Girls Bhayandar WhatsApp +91-9930687706, Best ServiceTamil Call Girls Bhayandar WhatsApp +91-9930687706, Best Service
Tamil Call Girls Bhayandar WhatsApp +91-9930687706, Best Service
 
Verification of thevenin's theorem for BEEE Lab (1).pptx
Verification of thevenin's theorem for BEEE Lab (1).pptxVerification of thevenin's theorem for BEEE Lab (1).pptx
Verification of thevenin's theorem for BEEE Lab (1).pptx
 
HOA1&2 - Module 3 - PREHISTORCI ARCHITECTURE OF KERALA.pptx
HOA1&2 - Module 3 - PREHISTORCI ARCHITECTURE OF KERALA.pptxHOA1&2 - Module 3 - PREHISTORCI ARCHITECTURE OF KERALA.pptx
HOA1&2 - Module 3 - PREHISTORCI ARCHITECTURE OF KERALA.pptx
 
DeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakesDeepFakes presentation : brief idea of DeepFakes
DeepFakes presentation : brief idea of DeepFakes
 
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLEGEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
GEAR TRAIN- BASIC CONCEPTS AND WORKING PRINCIPLE
 

Beginner's guide to linkers

  • 1. Beginner's Guide to Linkers Pinkus.Chang
  • 2. Outline • Question : compiler error • Naming of Parts: What's in a C File • Dissecting An Object File • Adding C++ to the picture • What’s the linker does part1 • What’s the linker does part2 • Adding C++ to the picture
  • 3. Question : compiler error A typical example of what triggers this explanation is when I help someone who has a link error like: Ans: ‘Almost certainly missing extern "C"'
  • 4. Naming of Parts: What's in a C File • Declaration vs Definition • 不直觀的特殊情況 – 用 static 修飾的局部變量實際上是全局變量,因為雖然它們僅在某個函 數中可見,但其生命週期存在於整個程序中 – 同樣,用 static 修飾的全局變量也被認為是全局的,儘管它們只能由它 們所在的文件內的函數訪問 • Compiler – 編譯器都只允許使用已經聲明(declaration)過的變量和函數,程序員對 編譯器的承諾:向它確保這個變量或函數已經在程序中的別處定義過了 • Linker – 兌現這一承諾 – 編譯器會留個空白(blank),這個「空白」(我們也稱之為「引用」 (reference))擁有與之相關聯的一個名字,但該名字對應的值還尚未可 知。
  • 6. Code Data Global Local Dynamic Initialized Uninitialized Initialized Uninitialized Declaration (extern)int fn_a() extern int z_global; N/A N/A N/A Definition static int fn_b() int fn_c() int x_global_init= 1; (All scope) static int y_global_init= 2; (at file scope) int x_global_unninit; (All scope) static int y_global_init= 2; (at file scope) int y_local_init= 1; (at function scope) int y_local_uninit; (at function scope) (int* p = malloc(sizeof(int));) 要process起來才會有橘色部分
  • 7. Dissecting An Object File : nm U: 該類型表示未定義的引用(undefined reference),即我們前文所提及的「空白」 (blanks)。對於示例中的目標文件,共有兩個未定義類型:「fn_a」 和 「z_global」。(有 些 nm 的版本還可能包括 section(譯註:即宏彙編中的區,後文直接使用section而不另作中文 翻譯)的名字,section的內容通常為 *UND* 或 UNDEF) t/T: 該類型指明了代碼定義的位置。t 和 T 用於區分該函數是定義在文件內部(t)還是定義 在文件外部(T)——例如,用於表明某函數是否聲明為 static。同樣的 ,有些系統包括 section ,內容形如.text d/D: 該類型表明當前變量是一個已初始化的變量,d 指明這是一個局部變量,D 則表示全局 變量。如果存在 section ,則內容形如 .data b/B: 對於非初始化的變量,我們用 b 來表示該變量是靜態(static)或是局部的(local),否 則,用 B 或 C 來表示。這時 section 的內容可能為.bss 或者 *COM*
  • 8. What The Linker Does : Part 1
  • 10. Linker main.c and c_parts.c main.c c_parts.c on a UNIX system, the linker is typically invoked with ld
  • 12. Duplicate Symbols • C++ – one definition rule • 即鏈接階段,一個符號有且只能定義一次 • C – 任何的函數或者已經初始化的全局變量,都有且只能有一次定義即鏈接階段,一個符號 有且只能定義一次 – But…未初始化的全局變量的定義可以看成是一種臨時性定義(a tentative definition) • Another language – Fortran : Linker 還得對付除 C/C++ 以外的其它語言 – one definition rule 並不適用 – As a result, it's actually quite common for UNIX linkers not to complain about duplicate definitions of symbols—at least, not when the duplicate symbol is an uninitialized global variable (this is sometimes known as the "relaxed ref/def model" of linking). If this worries you (and it probably should), check the documentation for your compiler linker—there may well be a --work-properly option that tightens up the behavior. For example, for the GNU toolchain the -fno-common option to the compiler forces it to put uninitialized variables into the BSS segment rather than generating these common blocks.
  • 13. What The Operating System Does 1.把code segment(text segment) 從 disk 搬到 memory ------------------------------------------------------ 2.把data segment (initial global variable )搬入 3.把bss segment( uninitialized global variable) 搬進去 但不用copy其值 預設都是0 ------------------------------------------------------ Local variable 不用linker幫忙 因為他生命週期只在process運行時 1.Local variable stack 2.Malloc等等變數  heap 3.Heap stack 撞再一起,就是滿了 123 1 2
  • 14. The size command didn't list a stack or heap segment for hello_world or hello_world.o. Why do you think that is? Review memory-layout 1.compiler and linker : gcc 2.Loader : exec Disk file Process memory
  • 15. What The Linker Does: Part 2
  • 16. Static libraries 1.Command : ar 2.libXXX.a 3.gcc –llibXXX.a 4.未解決符號表 it builds a list of the symbols it hasn't been able to resolve yet. When all of the explicitly specified objects are done with, the linker now has another place to look for the symbols that are left on this 5.Libraries的處理順序,相依性。 linker按命令行從左到右的順序進行處理,只有前一個庫處理結束了,才會繼續 處理下一個庫。換句話說,如果後一個庫中導入的目標文件依賴於前一個庫中的 某個符號,那麼鏈接器將無法進行自動關聯。
  • 17. File a.o b.o libx.a liby.a Object a.o b.o x1.o x2.ox3.o y1.o y2.o y3.o Definitions a1, a2, a3 b1, b2 x11, x12, x13 x21, x22, x23 X31, x32 y11, y12 y21, y22 y31, y32 Undefined references b2, x12 a3, y22 x23, y12 y11 y21 x31 gcc main.exe –lx –ly a.o b.o Step1: we can get b2 a3 ,but we still lose x12 y22 Step2: try to find x12 y22 in libx.a ,get x12 on x1.o => leak y22,x23,y12 Step3: try to find x23 and y12 because linker is still dealing with libx.a => leak y22 y12 y11 Setp4: try to find y21 y12 y11 on liby.a ,不過由於 y22 的存在,y2.o 無論如何都必須導入 => finish
  • 18. File a.o b.o libx.a liby.a Object a.o b.o x1.o x2.ox3.o y1.o y2.o y3.o Definitions a1, a2, a3 b1, b2 x11, x12, x13 x21, x22, x23 X31, x32 y11, y12 y21, y22 y31, y32 Undefined references b2, x12 a3, y22 y32 x23, y12 y11 y21 x31 Step5.y32 can find on liby.a , but ………. 1.x31 has been linked pervious time(libx.a) by this stage the linker has already finished with libx.a and would not find the definition (in x3.o) for this symbo Cyclic dependency We don’t load x3.o previous time.
  • 19. Shared libraries • Static libraries 缺點 – 1.浪費空間,所有代碼都要copy同一份 – 2.修改某個lib bug後,要全部重新link,才可已拿到修正後的code • Shared libraries – IOU(我欠你) • 等到process真正在運行的時候,才對這TAG處理 • 所以linker 發現 so 並不會把symbol包到exe 內,而是將symbol與其對應的libraries 記錄下來。 – Small linker: ld.so • Before program counter come to int main() • 負責檢查貼過標籤的內容,並完成鏈接的最後一個步驟:導入庫裡的代碼,並將所有符號都關 聯在一起 – the granularity of the link • 如果程序中只引用了共享庫裡的某個符號(比如,只使用了 libc.so 庫中的 printf),那麼整個共 享庫都將映射到程序地址空間中,這與靜態庫的行為完全不同,靜態庫中只會導入與該符號相 關的那個目標文件。
  • 20. Put another way…. • shared library =>can resolve to references between objects in the same library getting resolved. 因為整個so 內含的.o都會被放入,不會直有用到才放的問題 File a.o b.o libx.so liby.so Object a.o b.o x1.o x2.ox3.o y1.o y2.o y3.o Definitions a1, a2, a3 b1, b2 x11, x12, x13 x21, x22, x23 X31, x32 y11, y12 y21, y22 y31, y32 Undefined references b2, x12 a3, y22 y32 x23, y12 y11 y21 x31 x3.o can be linked when ld.so working
  • 21. Command ldd • ldd – shows the set of shared libraries that an executable (or a shared library) depends on – LD_LIBRARY_PATH • Typically, the loader looks for libraries in the list of directories held in the LD_LIBRARY_PATH environment variable.
  • 22. Adding C++ To The Picture 1. Function Overloading & Name Mangling 2. Initialization of Statics 3. Templates 4. Dynamically Loaded Libraries
  • 23. Function Overloading & Name Mangling 1.Good friends: nm 2.ii:integer ff: float dd:double The mangled names : nm --demangle
  • 24. need to add extern C The C++ code is actually looking for something like "_Z7findmaxii" but only finding "findmax" By the way, note that an extern "C" linkage declaration is ignored for member functions
  • 25. Initialization of Statics For this code, the (demangled) output of nm gives: Class 1.Constructor 2.W:weak symbols 3.所有這些全局對象的構造函數的調用順序並未定 義——因此,這完全取決於鏈接器的實現 way1: the sane way way2: the Sun way : 看不懂 GG, PASS~~
  • 26. Templates Way1: the sane way 對於第一種方法,即將每個實例函數代碼 展開,每個目標文件中都會包含它所調用 的所有模板函數的代碼,以上文的 C++ 文 件為例,目標文件內容如下: 目標函數將這兩個函數的定義標記成「弱 符號」(weak symbos),這表示當鏈接器 最終生成可執行程序時,將只留下所有重 複定義的其中之一,剩餘的定義都將棄之 不用
  • 27. Dynamically Loaded Libraries • 最終的鏈接操作可以延遲到程序真正運行的時 刻 • dlopen and dlsym • dlopen兩種選擇 – 解決導入庫的所有未定義符號(RTLD_NOW): 長 – 按遇到的順序一個個解決未定義符號 (RTLD_LAZY): 快,看有可能在process運作中,發 現undefined symbol 導致process crash
  • 28. Conclusion • C++ compiler 還有很多東西看不懂 EX:動態載入與C++特性的交互 Interaction with C++ Features • Shared libraries with GCC on Linux – http://cprogramming.com/ – 這篇不錯,寫得很入門。
  • 29. 常見command # All information readelf -a # check shared libraries readelf –a *.so | grep Shared # GCC version readelf -p .comment # CPU version readelf -h # module version readelf -p .mmodule_version # check symbol on execute file nm # check memory layout after linker size # Linking with a shared library ld # shows the set of shared libraries that an executable (or a shared library) depends on ldd # generate static libraries ar # telling GCC where to find the shared library gcc -L/home/username/foo -Wall -o test main.c -lfoo 'Note that the -lfoo option is not looking for foo.o, but libfoo.so' # header gcc -I
  • 30. addr2line # addr2line PC: /lib/libc-2.24.so [0x74a65bac] LR: /usr/lib/libadaptor.so.0.1.0 [0x759becd8] 754c8000-75a9a000 r-xp 00000000 b3:1f 146 /usr/lib/libkadaptor.so.0.1.0 75a9a000-75aaa000 ---p 005d2000 b3:1f 146 /usr/lib/libkadaptor.so.0.1.0 75aaa000-75b89000 rw-p 005d2000 b3:1f 146 /usr/lib/libkadaptor.so.0.1.0 >>> hex(0x759becd8-0x754c8000) '0x4f6cd8' $ addr2line -a -f -e ./libkadaptor.so 0x4f6cd8
  • 31. Reference • http://www.lurklurk.org/linkers/linkers.html • http://blog.jobbole.com/96225/ • http://www.geeksforgeeks.org/memory- layout-of-c-program/ • http://www.cprogramming.com/tutorial/shar ed-libraries-linux-gcc.html
  • 32. More Detail • John Levine, Linkers and Loaders: contains lots and lots of information about the details of linkers and loaders work, including all the things I've skipped here. There also appears to be an online version of it (or an early draft of it) here • Excellent link on the Mach-O format for binaries on Mac OS X [Added 27-Mar-06] • Peter Van Der Linden, Expert C Programming: excellent book which includes more information about how C codetransforms into a running program than any other C text I've encountered • Scott Meyers, More Effective C++: Item 34 covers the pitfalls of combining C and C++ in the same program (whether linker-related or not). • Bjarne Stroustrup, The Design and Evolution of C++: section 11.3 discusses linkage in C++ and how it came about • Margaret A. Ellis & Bjarne Stroustrup, The Annotated C++ Reference Manual: section 7.2c describes one particular name mangling scheme • ELF format reference [PDF] • Two interesting articles on creating tiny Linux executables and a minimal Hello World in particular. • "How To Write Shared Libraries" [PDF] by Ulrich Drepper has more details on ELF and relocation.

Hinweis der Redaktion

  1. [Text Segment] Code Segment,包含程式碼以及可以執行的指令 放在Memory中low level address的位置,避免被stack/heap overwrite 此區塊通常是shareable,比如說同時存在相同的Process時,只會共用一份text Read-only,避免程式自己修改自己的Instruction [Initialized Data Segment] 包含由Programmer自己定義的Global Variable/Static Variable 可以再細分為Read-Only和Read-Write Segment (global) int a = 1; //Read-Write const int a = 1; //Read-Only [Uninitialized Data Segment] A.K.A BSS segment 包含所有宣告為global/static的變數中,沒有任何初始值,或是初始值為0的變數 static int i; //BSS segment static int j = 0; //BSS segment [Stack] 存放"Automatic Variables",比方說function內的local variable 記錄function的堆疊狀況,比如caller的address,讓function結束後可以順利return。每個function都有自己的堆疊空間,不同function的變數不會互相干擾 [Heap] 可配置的動態記憶體空間,通常使用malloc/realloc/free函式 p1 = (char *)malloc(10); 根據以上的程式碼,會在Heap分配一塊10byte的空間。但是p1仍然是在Stack中,存放剛剛分配出來的空間的位置。 當Heap跟Stack的pointer collision,就會 Out Of Memory All threads share a common Heap.每個thread都有自己的stack,但是Heap是共用的 process 1. Text Segment: A text segment , also known as a code segment or simply as text, is one of the sections of a program in an object file or in memory, which contains executable instructions. As a memory region, a text segment may be placed below the heap or stack in order to prevent heaps and stack overflows from overwriting it. Usually, the text segment is sharable so that only a single copy needs to be in memory for frequently executed programs, such as text editors, the C compiler, the shells, and so on. Also, the text segment is often read-only, to prevent a program from accidentally modifying its instructions. 2. Initialized Data Segment: Initialized data segment, usually called simply the Data Segment. A data segment is a portion of virtual address space of a program, which contains the global variables and static variables that are initialized by the programmer. Note that, data segment is not read-only, since the values of the variables can be altered at run time. This segment can be further classified into initialized read-only area and initialized read-write area. For instance the global string defined by char s[] = “hello world” in C and a C statement like int debug=1 outside the main (i.e. global) would be stored in initialized read-write area. And a global C statement like const char* string = “hello world” makes the string literal “hello world” to be stored in initialized read-only area and the character pointer variable string in initialized read-write area. Ex: static int i = 10 will be stored in data segment and global int i = 10 will also be stored in data segment 3. Uninitialized Data Segment: Uninitialized data segment, often called the “bss” segment, named after an ancient assembler operator that stood for “block started by symbol.” Data in this segment is initialized by the kernel to arithmetic 0 before the program starts executing uninitialized data starts at the end of the data segment and contains all global variables and static variables that are initialized to zero or do not have explicit initialization in source code. For instance a variable declared static int i; would be contained in the BSS segment. For instance a global variable declared int j; would be contained in the BSS segment. 4. Stack: The stack area traditionally adjoined the heap area and grew the opposite direction; when the stack pointer met the heap pointer, free memory was exhausted. (With modern large address spaces and virtual memory techniques they may be placed almost anywhere, but they still typically grow opposite directions.) The stack area contains the program stack, a LIFO structure, typically located in the higher parts of memory. On the standard PC x86 computer architecture it grows toward address zero; on some other architectures it grows the opposite direction. A “stack pointer” register tracks the top of the stack; it is adjusted each time a value is “pushed” onto the stack. The set of values pushed for one function call is termed a “stack frame”; A stack frame consists at minimum of a return address. Stack, where automatic variables are stored, along with information that is saved each time a function is called. Each time a function is called, the address of where to return to and certain information about the caller’s environment, such as some of the machine registers, are saved on the stack. The newly called function then allocates room on the stack for its automatic and temporary variables. This is how recursive functions in C can work. Each time a recursive function calls itself, a new stack frame is used, so one set of variables doesn’t interfere with the variables from another instance of the function. 5. Heap: Heap is the segment where dynamic memory allocation usually takes place. The heap area begins at the end of the BSS segment and grows to larger addresses from there.The Heap area is managed by malloc, realloc, and free, which may use the brk and sbrk system calls to adjust its size (note that the use of brk/sbrk and a single “heap area” is not required to fulfill the contract of malloc/realloc/free; they may also be implemented using mmap to reserve potentially non-contiguous regions of virtual memory into the process’ virtual address space). The Heap area is shared by all shared libraries and dynamically loaded modules in a process.
  2. (未談及的技術問題:本節不涉及鏈接器「重定位(relocation)」這一重要特性的介紹。不同的程序大小也不同,因此,當動態庫在不同程序中使用時,將被映射成不同的地址空間,也就是說庫中所有的函數和變量在不同的程序中有不同的地址。如果所有訪問該地址之處,都使用相對地址(如「向後偏移1020字節」)而不是絕對地址(固定的某個地址值,如 0x102218BF),那這也不是個事兒,可現在我們要考慮的問題在於,現實並不總這麼盡如人意,當這種情況出現時,所有絕對地址都必須加上一個合適的偏移量——這就是重定位的概念。由於這一概念對C/C++程序員來說幾乎是完全透明的,並且鏈接中報的錯誤也幾乎不可能由重定位問題導致,因此下文將不會對此贅述。)
  3. 共享庫之所以使用更大的鏈接粒度是因為現代操作系統已經相當聰明了,當你想用靜態庫的時候,他為了節省一些硬盤空間,就採用小粒度的鏈接方式,但對於共享庫來說,不同的程序運行時共用同一個代碼段(但並不共同數據段和 bss 段,因為畢竟不同的程序使用不同的內存空間)。為了做到這一點,必須對整個庫的內容進行一次性映射,這樣才能保證庫內部的符號集中保存在一片連續的空間裡——否則,如果某個進程導入了 a.o 和 c.o, 另一個進程導入的是 b.o 和 c.o,那麼就沒什麼共同點可以供操作系統利用了。 The reason for this larger granularity is because modern operating systems are clever enough that you can save more than just the duplicate disk space that happens with static libraries; different running processes that use the same shared library can also share the code segment (but not the data/bss segments—two different processes could be in different places for their strtok after all). In order to do this, the whole library has to be mapped in one go, so that the internal references all line up to the same places—if one process pulled in a.o and c.o and another pulled in b.o and c.o, there wouldn't be any commonality for the OS to leverage.
  4. 在 C++ 中,構造過程所需完成的操作遠比「拷貝定值」複雜得多:在程序開始正常運行之前,類層次體系中各種構造函數裡的代碼都必須提前執行。 為了處理好這一切,編譯器在每一個C++文件的目標文件中都保存了一些額外信息,例如,保存了某個文件所需的構造函數列表。在鏈接階段,鏈接器把所有列表合成一張大表,通過一次次掃瞄該表來調用每個全局對象對應的構造函數。 The sane way 1.即將每個實例函數代碼展開,每個目標文件中都會包含它所調用的所有模板函數的代碼 2. max(int,int) and max(double,double)。 3.目標函數將這兩個函數的定義標記成「弱符號」(weak symbos) 這表示當鏈接器最終生成可執行程序時,將只留下所有重複定義的其中之一,剩餘的定義都將棄之不用 每個目標文件都將佔用更多的磁盤空間。 The SUN way Solaris 系統中的 C++ 編譯器所使用的方法 它不會在目標文件中包含任何跟模板相關的代碼,只將這些符號標記成「未定義」。等到了鏈接階段,鏈接器將所有模板實例化函數對應的未定義符號收集在一起,然後為它們生成相應的機器碼。 節省每個目標文件所佔的空間大小, 慢
  5. 這個例子中的C++文件調用了兩種類型的 max(int,int) 和 max(double,double),而對於另一個 C++ 文件,可能會調用該模板的其他實例化函數:比如max(float,float),甚至還有可能是更複雜的 max(MyFloatingPointClass,MyFloatingPointClass)。 模板的每一個實例化函數執行時使用的都是不同的機器碼,因此在程序的鏈接階段,編譯器和鏈接器需要確保程序調用的每個模板實例函數都擴展出相應類型的程序代碼(但對於未被調用的其他模板實例函數而言,不會有任何多餘的代碼生成,這樣可以避免程序代碼過度膨脹)。 那麼編譯器和鏈接器是如何做到這一切換呢?一般來說,有兩種實現方案:一種是將每個實例函數代碼展開,另一種是將實例化操作延遲到鏈接階段(我喜歡將這兩種方法分別稱作「普通方法」(the sane way)和 「Sun方法」(the sane way)(譯註:之所以取這個名字,是因為Solaris系統下的編譯器採用這樣的方法,而Solaris是當年Sun公司旗下最著名的操作系統。))。 對於第一種方法,即將每個實例函數代碼展開,每個目標文件中都會包含它所調用的所有模板函數的代碼,以上文的 C++ 文件為例,目標文件內容如下: