9.3 סקריפטים של הלינקר תרגול
תרגול - סקריפטים של הלינקר¶
תרגיל 1 - הlinker script של ברירת המחדל¶
- הריצו
ld --verboseוהפנו את הפלט לקובץ:ld --verbose > default_ld.txt - פתחו את הקובץ ומצאו:
- מהו ה-ENTRY? (מה נקודת הכניסה של ברירת המחדל?)
- מצאו את הסקשן
.text- אילו סקשנים נוספים נכנסים לתוכו מלבד*(.text)? - מצאו את הסקשן
.data- מה בא לפניו? מה אחריו? - חפשו את הביטוי
ALIGN- כמה פעמים הוא מופיע? על אילו ערכים מיישרים? - כתבו תוכנית hello world פשוטה, קמפלו אותה, ובדקו:
כמה סגמנטים מסוג LOAD יש? מה ההרשאות של כל אחד?
תרגיל 2 - linker script בסיסי¶
כתבו linker script בשם basic.ld שמגדיר:
- נקודת כניסה: main
- כתובת התחלה: 0x400000
- סקשנים: .text, .rodata, .data, .bss - בסדר הזה
צרו את הקובץ הבא:
// simple.c
#include <stdio.h>
const char message[] = "Hello from custom linker script!";
int counter = 0;
int main() {
counter++;
printf("%s (counter=%d)\n", message, counter);
return 0;
}
- קמפלו עם הlinker script שלכם:
gcc -T basic.ld simple.c -o simple - האם זה עובד? אם לא, למה? (רמז: חשבו מה קורה עם libc ועם _start)
- נסו:
gcc -T basic.ld -nostdlib simple.c -o simple- מה השגיאה? - הסבירו למה linker script מותאם עם libc דורש זהירות מיוחדת.
תרגיל 3 - תוכנית freestanding עם linker script¶
צרו תוכנית שרצה בלי libc. הlinker script:
/* free.ld */
ENTRY(_start)
SECTIONS {
. = 0x400000;
.text : {
*(.text)
*(.text.*)
}
.rodata : {
*(.rodata)
*(.rodata.*)
}
. = ALIGN(4096);
.data : {
*(.data)
}
.bss : {
*(.bss)
*(COMMON)
}
}
הקוד:
// free.c
static void my_exit(int code) __attribute__((noreturn));
static void my_exit(int code) {
__asm__ volatile("syscall" : : "a"(60), "D"(code));
__builtin_unreachable();
}
static long my_write(int fd, const void *buf, long len) {
long ret;
__asm__ volatile("syscall" : "=a"(ret) : "a"(1), "D"(fd), "S"(buf), "d"(len) : "rcx", "r11", "memory");
return ret;
}
void _start(void) {
my_write(1, "It works!\n", 10);
my_exit(0);
}
- קמפלו:
gcc -nostdlib -static -T free.ld free.c -o free_program - הריצו ובדקו שעובד.
- בדקו את גודל הקובץ עם
ls -laו-size free_program. השוו לתוכנית hello world סטטית רגילה. - הריצו
readelf -S free_program- כמה סקשנים יש? השוו לתוכנית רגילה. - הריצו
readelf -l free_program- כמה סגמנטים מסוג LOAD? מה ההרשאות?
תרגיל 4 - הגדרת סמלים בlinker script¶
שנו את הlinker script מתרגיל 3 כך שיגדיר סמלים:
ENTRY(_start)
SECTIONS {
. = 0x400000;
.text : {
_text_start = .;
*(.text)
*(.text.*)
_text_end = .;
}
.rodata : {
*(.rodata)
*(.rodata.*)
}
_text_size = _text_end - _text_start;
. = ALIGN(4096);
.data : {
_data_start = .;
*(.data)
_data_end = .;
}
.bss : {
_bss_start = .;
*(.bss)
*(COMMON)
_bss_end = .;
}
}
שנו את הקוד כדי להדפיס את הכתובות (תצטרכו לכתוב פונקציה שממירה מספר למחרוזת בלי libc):
- כתבו פונקציה
void print_hex(unsigned long value)שמדפיסה מספר בבסיס 16 באמצעותmy_write. - השתמשו ב-
extern char _text_start;וכו' כדי לגשת לסמלים שהגדרתם. - הדפיסו את כתובת ההתחלה והסיום של כל סקשן.
- חשבו את גודל סקשן
.textמתוך הסמלים, וודאו שהוא תואם את מה שreadelf מראה.
תרגיל 5 - סקשנים מותאמים אישית¶
צרו תוכנית freestanding עם סקשן מותאם אישית:
- הגדירו סקשן בשם
.startupשיכיל את הקוד של_start. - הגדירו סקשן בשם
.handlersשיכיל פונקציות טיפול. - כתבו linker script ששם את
.startupראשון (בכתובת 0x400000), ואחריו.text, ואחריו.handlers. - השתמשו ב-
__attribute__((section(".startup")))ו-__attribute__((section(".handlers")))כדי לשים פונקציות בסקשנים הנכונים. - הגדירו סמלים בlinker script שמסמנים את תחילת וסוף כל סקשן.
- וודאו עם readelf שהסקשנים מסודרים כצפוי.