Linking ও Relocation — ভাঙা address জোড়া লাগানো
Linking and Relocation
প্রতিটা .o ফাইল নিজের ভেতরের ঠিকানা জানে কিন্তু বাইরের প্রতিটা symbol-এর জায়গায় ফাঁকা ঘর রাখে — linker symbol resolution আর relocation-এর মাধ্যমে সেই ভাঙা ঠিকানাগুলো চূড়ান্ত মান দিয়ে জোড়া লাগায়।
আগে এটা বুঝি
গত লেসনে toy compiler প্রথম .s emit করল আর gcc দিয়ে চলল। কিন্তু
gcc gen.s -o gen-এর ভেতরে দুইটা প্রোগ্রাম চলেছে — assembler
আর linker। Assembler টেক্সট→byte ফেলে;
চলার-যোগ্য executable বানানোর কঠিন কাজ — প্রতিটা instruction-কে
চূড়ান্ত ঠিকানা দেওয়া, বাইরের call যুক্ত করা, startup বসানো —
linker-এর।
ভাঙা address মানে: call helper-এর rel32 ঘরে assembler লিখে রাখে
চারটা শূন্য — helper কোথায় বসবে সে জানে না। সে রেখে যায় একটা
চিঠি — “পরে এই ঘরে ঠিকানা বসাও” — relocation entry। Linker-ের
পুরো কাজ দুই প্রশ্নে: প্রতিটা নামের মালিক কে (symbol resolution),
আর ভাঙা ঘরে কী সংখ্যা বসবে (relocation)।
আর কেন এক ফাইলে সব লিখে এই ঝামেলাই না করা? কারণ পৃথক পৃথক translation unit ছাড়া: incremental build অসম্ভব (এক লাইন বদলালে পুরো project recompile), library ভাগ করা অসম্ভব (printf-এর সোর্স প্রতিটা প্রোগ্রামে কপি করতে হবে), আর ভিন্ন ভাষায় লেখা অংশ জোড়া দেওয়া অসম্ভব। পৃথকীকরণই দাবি করে যে কেউ একজন পরে জোড়া লাগাবে — সেই কেউ-ই linker।
মূল ধারণা
দুইটা টেবিল — কে কী দেয়, কে কী চায়
প্রতিটা relocatable object (.o) — elf-format লেসনের ET_REL ELF — ভেতরে রাখে ভূমিকায় বিপরীত দুইটা টেবিল।
Symbol table (.symtab) — “আমার কাছে কী আছে, আমি কী চাই”:
$ nm math.o
0000000000000000 T add_values # defined, .text, global
0000000000000000 D base # defined, .data, global
0000000000000034 t square_it # local (static)
U printf # undefinedঅক্ষরগুলোই সারাংশ — T/t: .text global/local, D/d: data, B: .bss, U: undefined, W/w: weak। Binding-এর তিন স্তর:
| Binding | দেখে | হয় |
|---|---|---|
| local | এই .o-র ভেতরেই | static |
| global | সব .o; এক নাম এক মালিক | সাধারণ ফাংশন |
| weak | সবাই, strong-এর কাছে হার মানে | __attribute__((weak)) |
Relocation table (.rela.text, .rela.data) — “এই জায়গাগুলোতে ফাঁকা ঘর, এই নিয়মে ভরো”:
$ readelf -r math.o
Relocation section '.rela.text' contains 3 entries:
Offset Info Type Symbol's Name + Addend
000000000016 ... R_X86_64_PLT32 printf - 4
00000000002a ... R_X86_64_PC32 .rodata - 4
000000000031 ... R_X86_64_PC32 base - 4প্রতিটা entry: কোথায় (Offset), কোন নাম (Symbol), কোন সূত্রে (Type)।
তিনটা সাধারণ relocation সূত্র
ABI-প্রতীক: S = symbol-এর চূড়ান্ত ঠিকানা, A = addend, P = ঠিক হওয়া ঘরের ঠিকানা।
| Type | সূত্র | কোথায় |
|---|---|---|
| R_X86_64_PC32 | S + A − P | RIP-relative data access |
| R_X86_64_PLT32 | S + A − P, S = PLT entry | call বাইরের_ফাংশন |
| R_X86_64_64 (“ABS64”) | S + A | .data-র .quad symbol |
PC32/PLT32 সূত্র এক — পার্থক্য অর্থে: PLT32 “দরকার হলে PLT entry দিয়ে যাও” (loader লেসনের গল্প); static exe-তে ফল এক। ABS64 পরম ঠিকানা, ৬৪ bit — কেবল data-তে, code-এ না (কোড PIC হতে চায়)।
উদাহরণ: লিংক-শেষে base = 0x402008, ঘর P = 0x401031, addend −4। PC32
সূত্রে ঘরে:
কোড থেকে যে RIP-relative access হবে, সেটা ঠিক তত দূরের base-কে
ছুঁবে — কোনো absolute address কোথাও লেখা হলো না।
Static linking — archive থেকে member টানা
.a archive = .o-গুচ্ছ + index (কোন member কোন symbol define করে)।
Resolution algorithm:
১. সরাসরি দেওয়া .o-গুলোর symbol টেবিল মিলাও
২. undefined প্রতিটা নামের জন্য archive index খোঁজো —
define করা member পেলে শুধু তা-ই টেনে আনো
৩. নতুন member নতুন undefined আনতে পারে → ধাপ ২ ফেরত
৪. আর কিছু টানা হচ্ছে না → থামো
৫. শেষে undefined বাকি → undefined reference;
একই strong নাম দুইবার → multiple definitionপরিণতি দুটি: archive-এর বেশিরভাগ member লিংক-ই হয় না (libc-র
হাজার ফাংশনের হাতful টুকুই আসে), আর link order গুরুত্বপূর্ণ —
-lfoo consumer-দের পরে না দিলে ধাপ ২-এ টানার পালাই আসে না।
ভেতরে কী ঘটছে
Link-time বনাম load-time — ঠিকানার দুই জন্ম
Symbol-এর ঠিকানা দুইবার জন্ম নেয়: link-time (linker-এর বাছাই করা virtual address) আর load-time (loader সত্যিই যেখানে বসাল)। Non-PIE যুগে দুটো এক — linker 0x400000 ধরলে loader সেখানেই বসাত। আধুনিক PIE (distro ডিফল্ট) চুক্তি বদলেছে:
link-time: সব section বসাও base 0 থেকে শুরু করে
→ প্রতিটা ঠিকানা আসলে "শুরু থেকে দূরত্ব"
load-time: kernel/ld.so একটা random base বেছে নেয় (ASLR)
→ পুরো image একসাথে সেই base-এ shift হয়
→ দূরত্ব অপরিবর্তিত, তাই PC-relative কোড সত্য থাকেPC-relative ঘর লোড-ঠিকানা থেকে স্বাধীন; ABS64 pointer-এর কাজ load-time-এও বাকি — R_X86_64_RELATIVE relocation loader base যোগ করে সারায়।
Object anatomy — এক নজরে আবার
elf-format লেসনের সেই ছকটা এবার linker-এর চোখ দিয়ে:
| Section | linker যা করে |
|---|---|
| .text/.rodata | সব .o-র টুকরো জুড়ে এক output section, RO-X segment-এ |
| .data/.bss | merge; .bss ফাইলে নেই, মেমরিতে zero-fill |
| .symtab | resolution-এ পড়ে, চূড়ান্ত বাইনারিতে প্রায়ই strip |
| .rela.text/data | সূত্র-তালিকা — merge-এর পরে apply |
| .debug_info | উপেক্ষা |
ক্রম জরুরি: আগে সবাইকে চূড়ান্ত ঠিকানা, পরে relocation সূত্র — উল্টো হলে S, P-ই অজানা।
- C সোর্স: helper();ঠিকানার ধারণাই নেই
- .s: call helper → .o: e8 00000000 + rela entryঘর বরাদ্দ, মান শূন্য
- Linker: S+A−P → e8 16 00 00 00link-time জন্ম
- Loader: base বাছে, image shiftload-time জন্ম — PC32 অক্ষত
দুইটা বিখ্যাত error — algorithm-এর দুই ব্যর্থতা
undefined reference — ধাপ ৫ ব্যর্থ: defined মেলেনি; কারণ তিন —
লেখাই হয়নি, library লিংক লাইনে নেই, বা order-ভুল। Multiple
definition — সংঘর্ষ: দুই strong global এক নামে; classic দোষী header-এ
int counter; include করা — সংজ্ঞা .c-তে থাকার নিয়মের উৎস এটাই।
উদাহরণ
তিন-ফাইল project পুরো দৈর্ঘ্যে
// mathutil.h
int add_values(int a, int b);
// mathutil.c
int base = 10;
static int square_it(int x) { return x * x; }
int add_values(int a, int b) { return base + square_it(a) + b; }
// main.c
#include <stdio.h>
int add_values(int a, int b);
int main(void) {
printf("result %d\n", add_values(3, 4));
return 0;
}$ gcc -c mathutil.c main.c
$ nm mathutil.o
T add_values # export
D base
t square_it # static — বাইরে অদৃশ্য
U printf # import
$ nm main.o
T main
U add_values
U printf
$ readelf -r main.o # PLT32 add_values -4, PLT32 printf -4, PC32 .rodata -4main.o জানে নামটা, জানে না জায়গাটা; mathutil.o উল্টোটা। জোড়া:
$ gcc mathutil.o main.o -o app
$ ./app
result 23শেষে ভাঙা ঘর সব ভরা — objdump-এ গণনা করা rel32; PIE বলে load address যা-ই হোক ঠিক থাকবে।
ছবিতে
Merge আর patch — এক ছবিতে
লিংকের আগে (তিনটা আলাদা জগৎ):
mathutil.o .text main.o .text
┌──────────────────┐ ┌──────────────────┐
│ add_values: │ │ main: │
│ ... │ │ call ??? │◄─ .rela.text:
│ call printf │◄─┐ │ call ??? │ offset 0x0b, PLT32,
└──────────────────┘ │ │ ... │ addend −4, নাম add_values
│ └──────────────────┘
.symtab: T add_values │
U printf ───┤ .symtab: T main
U add_values ──┐
│
লিংকের পরে (এক জগৎ, base 0x400000 ধরে): │
│
app .text │
┌───────────────────────────────────┐ │
│ 0x401000 main: │ │
│ call +offset ───────────────────┼──┐ │
│ 0x401020 add_values: │ │ │
│ ... │ │ │
│ call +offset ───────────────────┼──┼─ printf-এ (libc-তে)
└───────────────────────────────────┘ │
S(add_values)=0x401020 ◄───────┘
ঘর P=0x401007 → বসল S+A−Pলক্ষণ: প্রতিটা .text অক্ষুণ্ণ কপি পাশাপাশি; patch শুধু ভাঙা ঘরে;
printf-এর মতো library symbol ভাসমানই থাকতে পারে — বাঁধে dynamic
linking (পরের লেসন)।
নিজে চালিয়ে দেখুন
ভাঙুন ইচ্ছা করে — আর error-টা algorithm-এর ভাষায় পড়ুন
তিন-ফাইল project নিন, আগে স্বাস্থ্য-পরীক্ষা:
gcc -c mathutil.c main.c && gcc mathutil.o main.o -o app && ./app
# result 23ভাঙা ১ — missing definition। main.c-তে add_values → add_valuez
(শুধু main.c recompile):
gcc -c main.c && gcc mathutil.o main.o -o app/usr/bin/ld: main.o: in function `main':
main.c:(.text+0xb): undefined reference to `add_valuez'
collect2: error: ld returned 1 exit statusAlgorithm-ভাষায়: .text+0xb ঘরের নাম add_valuez, ধাপ ৫-এ defined
মেলেনি। Offset পর্যন্ত বলে দেয় — objdump-এ 0xb-ই সেই call।
ভাঙা ২ — duplicate definition। mathutil.c আর extra.c দুটোতেই
int base = 10;:
echo 'int base = 99;' > extra.c
gcc -c extra.c && gcc mathutil.o extra.o main.o -o app
# /usr/bin/ld: extra.o:(.data+0x0): multiple definition of `base`;
# mathutil.o:(.data+0x0): first defined here“first defined here” — সংঘর্ষ-বিন্দু: দুই strong global, এক নাম।
extra.c-এর base static করলেই error গায়েব — local binding global
symbol-table-তে ওঠেই না। Header-এ declaration, .c-তে definition —
নিয়মটা linker-ের সাথে সরাসরি চুক্তি।
undefined reference আর multiple definition — দুইটা ভয়ংকর দেখানো error-ই আসলে symbol resolution algorithm-এর দুইটা সুনির্দিষ্ট ব্যর্থতা-বিন্দু, আর error message-এর প্রতিটা শব্দ সেই algorithm-এর শব্দকোষ থেকেই আসে।
নিজে বানান
mini-linker — দুইটা .o থেকে চলমান executable
- দুইটা সরল relocatable object তৈরি করুন hand-written assembly থেকে
- pyelftools দিয়ে .text, .symtab, .rela.text পড়ুন
- .text-গুলো পরপর বসিয়ে merged image বানান, প্রতিটা global symbol-এর চূড়ান্ত ঠিকানা রেকর্ড করুন
- প্রতিটা PC32/PLT32 relocation-এ S + A − P গণনা করে ৪ byte patch করুন
- ELF header + একটা PT_LOAD program header লিখে ET_EXEC বানান, চালিয়ে exit code যাচাই করুন
লিংকারের হাত নিজে লাগাব — দুইটা .o, শুধু .text, শুধু PC-relative call। ইনপুট:
# start.s — ভবিষ্যতের entry point
.text
.globl _start
_start:
movl $7, %edi
call bump # নাম আছে, জায়গা নেই
movl %eax, %edi
movl $60, %eax # sys_exit
syscall
# bump.s — ৭-এর পরের সংখ্যা
.text
.globl bump
bump:
leal 1(%rdi), %eax
retas start.s -o start.o # অথবা: gcc -c start.s
as bump.s -o bump.o
readelf -r start.o # ... R_X86_64_PLT32 bump - 4 ← ভাঙা ঘরএবার লিংকার (~120 লাইন):
from elftools.elf.elffile import ELFFile
import struct, sys
BASE = 0x400000
PT_LOAD, ET_EXEC, EM_X86_64 = 1, 2, 62
PF_X, PF_R = 1, 4
def load_obj(path):
""".o থেকে .text bytes, symbols, relocations তুলে আনে"""
with open(path, 'rb') as f:
elf = ELFFile(f)
text = elf.get_section_by_name('.text').data()
syms = {}
symtab = elf.get_section_by_name('.symtab')
for s in symtab.iter_symbols():
if (s.name and s['st_shndx'] != 'SHN_UNDEF'
and s['st_info']['type'] == 'STT_FUNC'
and s['st_info']['bind'] == 'STB_GLOBAL'):
syms[s.name] = s['st_value']
relocs = []
for sec in elf.iter_sections():
if sec.header['sh_type'] == 'SHT_RELA':
for r in sec.iter_relocations():
sym = symtab.get_symbol(r['r_info_sym'])
relocs.append((r['r_offset'], r['r_info_type'],
sym.name, r['r_addend']))
return text, syms, relocs
objs = [load_obj(p) for p in sys.argv[1:3]]
# ধাপ ১ — layout: প্রতিটা .text-এর ভবিষ্যৎ ঠিকানা ঠিক করা
offsets, cursor = [], 0x100 # প্রথম 0x100 byte: দুইটা header
for text, _, _ in objs:
cursor = (cursor + 15) // 16 * 16 # ১৬-এ align
offsets.append(cursor)
cursor += len(text)
# ধাপ ২ — symbol resolution: নাম → চূড়ান্ত ঠিকানা
defined = {}
for (text, syms, _), off in zip(objs, offsets):
for name, val in syms.items():
if name in defined:
sys.exit(f"multiple definition of {name!r}")
defined[name] = BASE + off + val
# ধাপ ৩ — merge .text এক বড় image-এ
image = bytearray(cursor)
for (text, _, _), off in zip(objs, offsets):
image[off:off + len(text)] = text
# ধাপ ৪ — relocation: S + A − P দিয়ে ৪ byte patch
patched = 0
for (text, _, relocs), off in zip(objs, offsets):
for r_offset, rtype, name, addend in relocs:
if rtype not in (2, 4): # R_X86_64_PC32 / PLT32
sys.exit(f"অসমর্থিত type {rtype} ({name})")
if name not in defined:
sys.exit(f"undefined reference to {name!r}")
S = defined[name]
P = BASE + off + r_offset
rel = (S + addend - P) & 0xFFFFFFFF
site = off + r_offset
image[site:site + 4] = struct.pack('I', rel)
patched += 1
# ধাপ ৫ — executable emit: header দুটোই image-এর প্রথম 0x100 byte-এ
entry = defined['_start']
ehdr = b'\x7fELF' + bytes([2, 1, 1, 0]) + b'\x00' * 8
ehdr += struct.pack('HHIQQQIHHHHHH',
ET_EXEC, EM_X86_64, 1,
entry, 64, 0, 0,
64, 56, 1, 0, 0, 0)
phdr = struct.pack('IIQQQQQQ',
PT_LOAD, PF_R | PF_X, 0, BASE, BASE,
len(image), len(image), 0x1000)
image[0:len(ehdr)] = ehdr
image[len(ehdr):len(ehdr) + len(phdr)] = phdr
# এখন সার্বিক নিয়ম: file offset X ↔ virtual address BASE + X
out = sys.argv[3] if len(sys.argv) > 3 else 'mini'
with open(out, 'wb') as f:
f.write(bytes(image))
import os
os.chmod(out, 0o755)
print(f"লিংক শেষ: {patched}টি relocation patch, entry={entry:#x}")python3 minilink.py start.o bump.o mini
# লিংক শেষ: 1টি relocation patch, entry=0x400100
./mini; echo "exit=$?"
# exit=8চলল — bump(7)=8 exit code-এ। ভাঙা ঘরে কী বসল, নিজের চোখে:
$ xxd -s 0x100 -l 24 mini
00000100: bf07 0000 00e8 1600 0000 89c7 b83c 0000
^^^^^^^^^^^^
call-এর rel32 = 0x00000016 = 22হাতে যাচাই: bump=0x400120, P=0x400106 → S+A−P = 0x400120−4−0x400106 = 22; execution-এ next-instruction 0x40010a + 22 = 0x400120 — ঠিক bump। এই চার byte-ই linking-এর হৃদস্পন্দন।
নিজে বাড়ান:
- .data merge + R_X86_64_64 (ABS64):
.quad bump-এর জন্যS + Aনিজে বাস্তবায়ন করুন - ইচ্ছা করে ভাঙুন — bump.o বাদ (আপনার undefined reference message), দুইটা start.o (multiple definition)
readelf -h/-l miniদিয়ে নিজের header ফিল্ড হাতে যাচাই — e_type, e_entry, PT_LOAD p_vaddr
বাস্তব সিস্টেমে
Linker যেখানে বাস্তবে দৌড়ায়
mold, lld, gold। বড় C++ project-এর মিনিট-লম্বা লিংক parallel linker-রা দশ-বিশ গুণ দ্রুত করেছে — বাধা ছিল বাস্তবায়নে, algorithm-এ না।
Game console/embedded। Console SDK প্রায় পুরোটাই static — predictable, ছোট runtime, loader attack-surface নেই।
LTO। .o-তে IR রেখে linker-ই পুরো প্রোগ্রাম optimize করে — compile/link সীমানা নরম।
Plugin/.so। Runtime-এ object জোড়া — এই লেসনের নিয়মেরই বিস্তৃতি, পরের লেসনের খাবার।
যে ভুলগুলো সবাই করে
“Linker শুধু ফাইলগুলো পাশাপাশি জুড়ে দেয়।”
জোড়া সবচেয়ে সস্তা অংশ। আসল দুই কাজ: resolution (নামের চূড়ান্ত মালিক — ব্যর্থলে undefined/multiple error) আর relocation (ভাঙা ঘরে সূত্র-মাফিক সংখ্যা); সাথে section order, alignment, versioning-এর হাজার সিদ্ধান্ত।
“Static library লিংক করলে পুরো .a বাইনারিতে ঢুকে যায়।”
উল্টো — archive extraction নির্ভর করে শুধু দরকারি member-এ। libc.a কয়েক মেগাবাইট, অথচ একটা hello world-এ তার থেকে আসে কয়েক কিলোবাইট: যে .o member-গুলো চাওয়া symbol define করে শুধু সেগুলোই টানা হয় (এবং তাদের নিজেদের দরকারগুলোও পর্যায়ক্রমে)। “লিংক হলোই সব ঢুকল” ভাবলে বাইনারি সাইজ বোঝা অসম্ভব হয়ে যায়।
“'undefined reference' মানেই library install করা হয়নি।”
তিন কারণেই এক error: define-ই নেই; লিংক লাইনে ফাইল নেই; অথবা
order-ভুল — ফাইল আছে কিন্তু consumer-দের পরে, index-খোঁজার পালা
আসেনি। তৃতীয়টায় -lfoo সরালেই ঠিক — package লাগে না।
“Relocation শুধু link-time-এ একবার হয়।”
Static non-PIE-তে হ্যাঁ। PIE/.so-তে অনেক relocation “load-time-এ base যোগ করো” বলে থাকে — loader প্রতি launch-এ প্রয়োগ করে; lazy GOT entry তো প্রথম call-এও পূর্ণ হয়। Link-time প্রথম জন্ম, শেষ নয়।
বুঝেছেন কি না দেখুন
1nm-এর আউটপুটে T, t, D, B, U, W — প্রতিটা অক্ষর কী বোঝায়? কোনটা
দেখলে বুঝবেন ওই .o অন্য কারো উপর নির্ভরশীল?
স্মরণ
T/t: .text global/local; D/d: data; B: .bss; U: undefined — বাইরে আসবে; W/w: weak। নির্ভরশীলতার প্রমাণ শুধু U — প্রতিটা U একটা ভাঙা ঘর, resolution-এ পূর্ণ হবে।
2একটা R_X86_64_PC32 entry: symbol base, addend −4, ঘরটা বসবে
0x401031-এ। লিংক-শেষে base-এর ঠিকানা দাঁড়াল 0x402008। ঘরে
কী সংখ্যা (hex) বসবে? সেই ঘর থেকে RIP-relative access-টা কীভাবে
base-এ পৌঁছায়?
প্রয়োগ
base, addend −4, ঘরটা বসবে
0x401031-এ। লিংক-শেষে base-এর ঠিকানা দাঁড়াল 0x402008। ঘরে
কী সংখ্যা (hex) বসবে? সেই ঘর থেকে RIP-relative access-টা কীভাবে
base-এ পৌঁছায়?0x402008 + (−4) − 0x401031 = 0xFD3; little-endian bytes d3 0f 00 00।
Access-এ RIP (= P+4 = 0x401035) + 0xFD3 = 0x402008 — ঠিক base। Addend
−4-ই “next instruction”-এর সমন্বয়, তাই call/lea-তে প্রায়ই −4।
3gcc main.o -lfoo ব্যর্থ — undefined reference to foo_fn। কিন্তু
gcc -lfoo main.o সফল! কী ঘটছে, আর কোন ধাপে?
যুক্তি
gcc main.o -lfoo ব্যর্থ — undefined reference to foo_fn। কিন্তু
gcc -lfoo main.o সফল! কী ঘটছে, আর কোন ধাপে?Linker বাম→ডান চলে: -lfoo-এর সময় foo_fn-এর চাহিদা এখনো জমেনি
(main.o অপঠিত), তাই member টানা হয়নি; পরে U খালি থেকে যায়। উল্টো
করলে U আগে জমে, index খোঁজা যায় — মেটে। নিয়ম: consumer আগে,
provider পরে।
4PIE executable-এর সব PC32 relocation কি load-time-এ আবার patch করতে
হয়? না হলে বাইনারি অন্য ঠিকানায় লোড হলে কীভাবে সঠিক থাকে?
যুক্তি
না — PC32 ঘরগুলো লিংক-শেষে চূড়ান্ত, লোডে কোনো স্পর্শ হয় না।
কারণ সূত্রটা দূরত্বের: S + A − P-এ S আর P দুটোই একই base থেকে
মাপা, তাই base বদলালে দুটো একই পরিমাণে বদলায় আর পার্থক্য
অপরিবর্তিত থাকে। যে ঘরগুলো load-time-এ দরকার সেগুলো absolute
(R_X86_64_64-জাতীয়) — সেগুলোর জন্যই dynamic relocation তালিকা
রেখে যেতে হয়। এই অর্থনীতিই PIE-কে সস্তা করেছে: বেশিরভাগ ঘর
একবারই ঠিক হয়।
5আপনার mini-linker-এ এখন দুইটা সীমাবদ্ধতা: .data merge নেই আর
alignment নেই। কোনটা আগে যোগ করবেন এবং কেন? .data যোগ করলে কোন
নতুন relocation type সামলাতে হবে?
ডিজাইন
Alignment আগে — কারণ এটা সঠিকতার প্রশ্ন: x86-64-এ misaligned code
চলে বটে, কিন্তু .data-র ৮-byte ভ্যালু misaligned হলে performance
ক্ষতি এবং ভবিষ্যতে atomic/SIMD অপারেশনে fault-এর ঝুঁকি; প্রতিটা
section-এর sh_addralign মেনে cursor round-up করাই সস্তা সংশোধন।
.data merge-এ পালা আসবে R_X86_64_64 (ABS64)-এর — সূত্র S + A
(পার্থক্য নয়, পরম মান) — এবং সঙ্গে সিদ্ধান্ত: এই ঘর PIE-তে
load-time-এ সরাতে হবে, তাই output-এ dynamic relocation entry-ও
লিখতে হবে। এক ধাপে দুইটা নতুন ধারণা — তাই আগে alignment।
এরপর কী
এরপর কী
Mini-linker সরাসরি-চলা ET_EXEC বানাল — ঠিকানা লিংক-টাইমেই চূড়ান্ত
বলে। কিন্তু gcc-বানানো বাইনারিতে kernel PT_INTERP দেখে dynamic
linker-কে ডাকে — “অসম্পূর্ণ relocation তোমার।” সেই দ্বিতীয় জোড়া,
GOT/PLT lazy binding, ./a.out থেকে main() পর্যন্ত যাত্রা — পরের
লেসনে।
আরও পড়ুন
- Linkers and Loaders — John R. Levine · Symbol resolution আর relocation-এর সবচেয়ে পঠনীয় প্রামাণ্য আলোচনা — Chapter 5 ও 6 এই লেসনের মেরুদণ্ড
- System V ABI — Generic ABI, Chapter 4 (Object Files) · Relocation entry-র সংজ্ঞা ও x86-64 type-সূত্র — S, A, P-এর উৎস
- "Linkers" essay series — Ian Lance Taylor · gold linker-এর রচয়িতার বাস্তব-linker সিদ্ধান্তের ধারাবাহিক