לדלג לתוכן

9.2 הלינקר תרגול

תרגול - הלינקר

תרגיל 1 - פתרון סמלים ידני

צרו שלושה קבצים:

// main.c
#include <stdio.h>

extern int counter;
void increment(void);
void print_counter(void);

int main() {
    print_counter();
    increment();
    increment();
    increment();
    print_counter();
    return 0;
}
// counter.c
int counter = 0;

void increment(void) {
    counter++;
}
// printer.c
#include <stdio.h>

extern int counter;

void print_counter(void) {
    printf("Counter = %d\n", counter);
}
  1. קמפלו כל קובץ בנפרד ל-.o ובדקו את הסמלים של כל קובץ עם nm. לכל קובץ, רשמו אילו סמלים הוא מגדיר (T/D) ואילו הוא צריך (U).
  2. חברו את שלושת הקבצים לקובץ הרצה והריצו. מה הפלט?
  3. מה יקרה אם נשמיט את counter.o מהלינקוג'? נסו והסבירו את השגיאה.
  4. מה יקרה אם נשמיט את printer.o? נסו והסבירו.

תרגיל 2 - סמלים חזקים וחלשים

צרו שני קבצים:

// strong.c
#include <stdio.h>

int value = 42;

void show(void) {
    printf("value = %d\n", value);
}
// weak.c
#include <stdio.h>

int value;

int main() {
    show();
    value = 100;
    show();
    return 0;
}
  1. קמפלו וחברו את שני הקבצים. מה הפלט?
  2. שנו את weak.c כך ש-int value; יהפוך ל-int value = 99;. מה קורה כשמנסים לחבר?
  3. הסבירו: למה במקרה הראשון הלינקר לא נתן שגיאה, אבל במקרה השני כן?
  4. איך תפתרו את הבעיה אם אתם צריכים משתנה value בשני הקבצים אבל כל קובץ צריך את הערך שלו?

תרגיל 3 - יצירת ספריה סטטית

צרו ספריה סטטית שמממשת פעולות על מחרוזות:

// mystring.h
#ifndef MYSTRING_H
#define MYSTRING_H
int my_strlen(const char *s);
char *my_strcpy(char *dest, const char *src);
int my_strcmp(const char *s1, const char *s2);
void my_strrev(char *s);  // היפוך מחרוזת
#endif
  1. כתבו את המימוש של כל ארבע הפונקציות בקובץ mystring.c (בלי להשתמש בפונקציות של libc).
  2. צרו ספריה סטטית libmystring.a מקובץ האובייקט.
  3. כתבו תוכנית main.c שמשתמשת בכל ארבע הפונקציות ומדפיסה תוצאות.
  4. קמפלו את התוכנית עם -L. -lmystring.
  5. הריצו ar -t libmystring.a כדי לראות את תוכן הספריה.
  6. הריצו nm libmystring.a כדי לראות את הסמלים בספריה.

תרגיל 4 - בעיות סדר ספריות

צרו שלושה קבצים שתלויים אחד בשני:

// liba.c
#include <stdio.h>
int func_b(int x);  // מוגדר ב-libb

int func_a(int x) {
    printf("func_a(%d)\n", x);
    if (x > 0) return func_b(x - 1);
    return x;
}
// libb.c
int func_a(int x);  // מוגדר ב-liba

int func_b(int x) {
    if (x > 0) return func_a(x - 1);
    return x * 2;
}
// main.c
#include <stdio.h>
int func_a(int x);

int main() {
    printf("Result: %d\n", func_a(5));
    return 0;
}
  1. צרו שתי ספריות סטטיות: liba.a (מ-liba.o) ו-libb.a (מ-libb.o).
  2. נסו לחבר: gcc main.o -L. -la -lb -o program. מה קורה?
  3. נסו בסדר הפוך: gcc main.o -L. -lb -la -o program. מה קורה?
  4. מצאו דרך לחבר בהצלחה (רמז: --start-group/--end-group או חזרה על ספריות).

תרגיל 5 - חקירת הlinker script

  1. הריצו ld --verbose כדי לראות את הlinker script של ברירת המחדל. מצאו:
  2. מהי נקודת הכניסה (ENTRY)?
  3. מה כתובת ההתחלה של סגמנט הקוד?
  4. אילו סקשנים נכנסים לסגמנט הקוד (text segment)?

  5. כתבו תוכנית פשוטה, קמפלו אותה, ובדקו:

    readelf -h program | grep Entry
    nm program | grep _start
    nm program | grep main
    

    מה הכתובת של _start? מה הכתובת של main? מי קודם?

  6. הריצו gcc main.c -Wl,-Map=output.map -o program. פתחו את output.map ומצאו:

  7. איפה סקשן .text הוצב
  8. איפה סקשן .data הוצב
  9. מה הגודל של כל סקשן