Wie kann das Hinzufügen eines Funktionsaufrufs dazu führen, dass andere Symbole beim Verknüpfen undefiniert werden?

Lesezeit: 20 Minuten

Benutzer-Avatar
owst

Ich hoffe, dass jemand helfen kann, das zu beheben, was ich für ein Linker-Skript-Problem halte.

Ich stoße auf ein seltsames Problem, nachdem ich einer neuen Funktion einen Aufruf hinzugefügt habe. Ohne den Funktionsaufruf werden meine Objektdateien korrekt verknüpft, aber mit dem hinzugefügten neuen Funktionsaufruf erhalte ich einen undefinierten Verweis auf ein Symbol aus einer anderen Objektdatei (ich habe mit objdump überprüft, ob es tatsächlich vorhanden ist).

Wenn ich bei vorhandenem Funktionsaufruf alle Objektdateien zuerst mit ld -r verknüpfe (um eine verschiebbare Ausgabe zu erhalten) und dann mein Link-Skript verwende, gibt es seltsamerweise keine undefinierten Verweise, aber es scheint, dass das Link-Skript seitdem ignoriert wird Die Ausgangsbinärdatei hat nicht den richtigen Einstiegspunkt.

Meine (Cross-Compiler) ld-Version:

> i586-elf-ld –version
GNU ld (GNU Binutils) 2.20.1.20100303

Meine Versuche zu beweisen, dass das “fehlende” Symbol vorhanden ist:

> i586-elf-ld -T link.ld -o kernel32.bin kernel_loader.o main.o stdio.o common.o gdt.o gdt.bin -y putch

main.o: reference to putch  
stdio.o: definition of putch  
main.o: In function `main':  
main.c:(.text+0x1f): undefined reference to `putch'

NB (als ich diese Ausgabe produzierte, verwendete ich einen Dateinamen von gdt.bin für den mit NASM kompilierten Assembler, es ist wirklich nur eine weitere .o-Datei)

Ich kann das Symbol sehen, das in der entsprechenden Objektdatei “fehlt”:

> i586-elf-objdump -ht stdio.o
stdio.o: Dateiformat elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         000002f9  00000000  00000000  00000034  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         0000000c  00000000  00000000  00000330  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000008  00000000  00000000  0000033c  2**2
                  ALLOC
  3 .comment      00000012  00000000  00000000  0000033c  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
00000000 l    df *ABS*  00000000 stdio.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .comment       00000000 .comment
00000000 g     F .text  00000016 strlen
00000016 g     F .text  0000005c scroll
00000008 g     O .data  00000004 numrows
00000004 g     O .bss   00000004 ypos
00000004 g     O .data  00000004 numcols
00000004       O *COM*  00000004 screen_mem
00000000         *UND*  00000000 memcpy
00000000         *UND*  00000000 memsetw
00000072 g     F .text  0000007d newline
00000000 g     O .bss   00000004 xpos
000000ef g     F .text  0000002e writech
00000000 g     O .data  00000004 colour
0000011d g     F .text  00000061 cls
0000017e g     F .text  00000010 init_video
0000018e g     F .text  00000133 putch
000002c1 g     F .text  00000037 puts
000002f8 g     F .text  00000001 set_text_colour

Und die Objektdatei mit nicht aufgelöster Referenz:

> i586-elf-objdump -ht main.o

main.o:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .text         0000007f  00000000  00000000  00000034  2**2
                  CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
  1 .data         00000000  00000000  00000000  000000b4  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  2 .bss          00000000  00000000  00000000  000000b4  2**2
                  ALLOC
  3 .rodata.str1.1 00000024  00000000  00000000  000000b4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .comment      00000012  00000000  00000000  000000d8  2**0
                  CONTENTS, READONLY
SYMBOL TABLE:
00000000 l    df *ABS*  00000000 main.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .rodata.str1.1 00000000 .rodata.str1.1
00000000 l    d  .comment       00000000 .comment
00000000 g     F .text  0000007f main
00000000         *UND*  00000000 init_video
00000000         *UND*  00000000 gdt_install
00000000         *UND*  00000000 putch
00000000         *UND*  00000000 puts
00000018       O *COM*  00000001 gdt
00000006       O *COM*  00000001 gdtp

Mein Link-Skript (nicht sicher, ob es relevant sein wird):

OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata*)
    . = ALIGN(4096);
  }
  .data . : AT(data)
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss . : AT(bss)
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}

Wenn ich den Aufruf von putch in main.c auskommentiere, erhalte ich stattdessen undefinierte Verweise auf puts … wenn ich den Aufruf von gdt_install entferne, keine Fehler!

gdt_install ist in der C-Datei, aber gdt_install ruft eine Funktion auf, die in gdt.asm definiert ist.

void gdt_install() {
    /* ... */
    gdt_reset();
}

[bits 32]
[section .text]
global gdt_reset
extern gdtp

gdt_reset:
    lgdt [gdtp]
    mov ax, 0x10      ; 0x10 offset for data segment (sizeof(struct gdt_entry) * 2)
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp 0x08:gdt_reset2   ; 0x08 offset for code segment (sizeof(struct gdt_entry))
gdt_reset2:
    ret              ; ret back to C

Um zu versuchen, die Ursache weiter zu diagnostizieren, habe ich herumgespielt und versucht, die Fehler zu reproduzieren. Wenn ich den Funktionsaufruf gdt_install() an eine bestimmte Stelle im Quellcode verschiebe, erhalte ich keine Fehler und alles funktioniert einwandfrei:

int main() {        

    init_video();

    putch('A');

    puts("<- print a single char there...\n");

    gdt_install();
    puts("asdf\n\n");

    int i;

    for (i = 0; i < 15; ++i) {
        if (i % 2 == 0) {
            puts("even\n");
        } else {
            puts("odd\n");
        }
    }

    return 0;
}

Wenn ich den Aufruf über den ersten puts()-Aufruf verschiebe, erhalte ich undefinierte Referenzen für Puts!:

...
init_video();

putch('A');

gdt_install();

puts("<- print a single char there...\n");

puts("asdf\n\n");

...



i586-elf-ld -T link.ld -o kernel32.bin kernel_loader.o main.o stdio.o common.o gdt.o gdt_asm.o
main.o: In function `main':
main.c:(.text+0x2b): undefined reference to `puts'
main.c:(.text+0x37): undefined reference to `puts'
main.c:(.text+0x51): undefined reference to `puts'
main.c:(.text+0x63): undefined reference to `puts'

Als nächstes, wenn ich den Aufruf über putch () verschiebe, wird eine undefinierte Referenz auf putch (wo ich ursprünglich den Aufruf hatte) verursacht:

...
init_video();

gdt_install();

putch('A');

puts("<- print a single char there...\n");

puts("asdf\n\n");

...

main.o: In function `main':
main.c:(.text+0x1f): undefined reference to `putch'

Und schließlich bewirkt oberhalb von init_video() eine undefinierte Referenz auf init_video:

...
gdt_install();

init_video();

putch('A');

puts("<- print a single char there...\n");

puts("asdf\n\n");

...

main.o: In function `main':
main.c:(.text+0x15): undefined reference to `init_video'

Was in aller Welt verursacht diesen Fehler? Es ist, als würde der gdt_install-Aufruf irgendwie andere Symbole “korrumpieren” … Ich konnte in keinem Dokument einen Hinweis darauf finden, aber gibt es eine Möglichkeit, dass der gdt_install-Funktionsaufruf dazu führen könnte, dass eine Linker-“Grenze” überschritten wird, beschädigt anderer Code?

Ist jemandem ein solches Problem begegnet oder hat er eine Idee zur weiteren Untersuchung? Ich habe im osdev-Forum gepostet: http://forum.osdev.org/viewtopic.php?f=1&t=22227 aber viel Glück hatte ich nicht.

Vielen Dank

Bearbeiten:

Ich bin mir nicht sicher, ob es relevant ist, aber wenn ich das Link-Skript beim Linken weglasse, verschwinden alle vorherigen Fehler … (allerdings kann mein Bootloader dann den Kernel nicht aufrufen, da er keine Elf-Binärdateien versteht).

Wie gewünscht, hier ist die main.c-Datei vor und nach der Vorverarbeitung und disassembliert aus der kompilierten main.o-Datei.

vor der Vorverarbeitung:

#include <stdio.h>
#include <common.h>
#include <gdt.h>

int main() {        
    init_video();

    putch('A');

    gdt_install();

    puts("<- print a single char there...\n");

    puts("asdf\n\n");

    int i;

    for (i = 0; i < 15; ++i) {
        if (i % 2 == 0) {
            puts("even\n");
        } else {
            puts("odd\n");
        }
    }

    return 0;
}

Nach der Vorverarbeitung:

i586-elf-gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -fno-inline -nostdinc -nostdlib -fsigned-char -nostartfiles -nodefaultlibs -fno-builtin -fno-stack-protector -I./include -E main.c
# 1 "main.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "main.c"
# 1 "./include/stdio.h" 1



# 1 "./include/common.h" 1



typedef unsigned short ushort;
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
typedef int size_t;

void *memcpy(void *dst, const void *src, size_t n);
void *memset(void *dst, const char val, size_t n);
void *memsetw(void *dst, const ushort val, size_t n);
void *memseti(void *dst, const int val, size_t n);
# 5 "./include/stdio.h" 2

void cls();
void writech(char c);
void putch(char c);
void puts(char *str);
void set_text_colour(uchar f, uchar b);
void init_video();
size_t strlen(char *str);
# 2 "main.c" 2

# 1 "./include/gdt.h" 1





struct gdt_entry {
    ushort limit_low;
    ushort base_low;
    uchar base_middle;
    uchar access;
    uchar granularity;
    uchar base_high;
} __attribute__((packed));

struct gdt_ptr {
    ushort limit;
    uint base;
} __attribute__((packed));

void gdt_set_gate(int n, ulong base, ulong limit, uchar access, uchar gran);
void gdt_install();

extern void gdt_reset();
# 4 "main.c" 2

int main() {
    init_video();

    putch('A');

    gdt_install();

    puts("<- print a single char there...\n");

    puts("asdf\n\n");

    int i;

    for (i = 0; i < 15; ++i) {
        if (i % 2 == 0) {
            puts("even\n");
        } else {
            puts("odd\n");
        }
    }

    return 0;
}

Nochmals editieren:

Vielen Dank an nategoose für den Vorschlag von -g3, um eine schönere Disassemblierungsausgabe zu erhalten:

main.o:     file format elf32-i386

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 main.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .rodata.str1.4 00000000 .rodata.str1.4
00000000 l    d  .rodata.str1.1 00000000 .rodata.str1.1
00000000 l    d  .stab  00000000 .stab
00000000 l    d  .stabstr   00000000 .stabstr
00000000 l    d  .comment   00000000 .comment
00000000 g     F .text  0000007f main
00000000         *UND*  00000000 init_video
00000000         *UND*  00000000 putch
00000000         *UND*  00000000 gdt_install
00000000         *UND*  00000000 puts



Disassembly of section .text:

00000000 <main>:
#include <stdio.h>
#include <common.h>
#include <gdt.h>

int main() {        
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  -0x4(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   53                      push   %ebx
   e:   51                      push   %ecx
    init_video();
   f:   e8 fc ff ff ff          call   10 <main+0x10>

    putch('A');
  14:   83 ec 0c                sub    $0xc,%esp
  17:   6a 41                   push   $0x41
  19:   e8 fc ff ff ff          call   1a <main+0x1a>

    gdt_install();
  1e:   e8 fc ff ff ff          call   1f <main+0x1f>

    puts("<- print a single char there...\n");
  23:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  2a:   e8 fc ff ff ff          call   2b <main+0x2b>

    puts("asdf\n\n");
  2f:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
  36:   e8 fc ff ff ff          call   37 <main+0x37>
  3b:   83 c4 10                add    $0x10,%esp

    int i;

    for (i = 0; i < 15; ++i) {
  3e:   bb 00 00 00 00          mov    $0x0,%ebx
        if (i % 2 == 0) {
  43:   f6 c3 01                test   $0x1,%bl
  46:   75 12                   jne    5a <main+0x5a>
            puts("even\n");
  48:   83 ec 0c                sub    $0xc,%esp
  4b:   68 07 00 00 00          push   $0x7
  50:   e8 fc ff ff ff          call   51 <main+0x51>
  55:   83 c4 10                add    $0x10,%esp
  58:   eb 10                   jmp    6a <main+0x6a>
        } else {
            puts("odd\n");
  5a:   83 ec 0c                sub    $0xc,%esp
  5d:   68 0d 00 00 00          push   $0xd
  62:   e8 fc ff ff ff          call   63 <main+0x63>
  67:   83 c4 10                add    $0x10,%esp

    puts("asdf\n\n");

    int i;

    for (i = 0; i < 15; ++i) {
  6a:   43                      inc    %ebx
  6b:   83 fb 0f                cmp    $0xf,%ebx
  6e:   75 d3                   jne    43 <main+0x43>
            puts("odd\n");
        }
    }

    return 0;
}
  70:   b8 00 00 00 00          mov    $0x0,%eax
  75:   8d 65 f8                lea    -0x8(%ebp),%esp
  78:   59                      pop    %ecx
  79:   5b                      pop    %ebx
  7a:   5d                      pop    %ebp
  7b:   8d 61 fc                lea    -0x4(%ecx),%esp
  7e:   c3                      ret    

Und jetzt die neue Ausgabe von einem sauberen Make:

$ make
nasm -f elf kernel_loader.asm -o kernel_loader.o
i586-elf-gcc -Wall -O0 -fstrength-reduce -fomit-frame-pointer -fno-inline -nostdinc -nostdlib -fsigned-char -nostartfiles -nodefaultlibs -fno-builtin -fno-stack-protector -I./include -g3 -c main.c
i586-elf-gcc -Wall -O0 -fstrength-reduce -fomit-frame-pointer -fno-inline -nostdinc -nostdlib -fsigned-char -nostartfiles -nodefaultlibs -fno-builtin -fno-stack-protector -I./include -g3 -c stdio.c
i586-elf-gcc -Wall -O0 -fstrength-reduce -fomit-frame-pointer -fno-inline -nostdinc -nostdlib -fsigned-char -nostartfiles -nodefaultlibs -fno-builtin -fno-stack-protector -I./include -g3 -c common.c
i586-elf-gcc -Wall -O0 -fstrength-reduce -fomit-frame-pointer -fno-inline -nostdinc -nostdlib -fsigned-char -nostartfiles -nodefaultlibs -fno-builtin -fno-stack-protector -I./include -g3 -c gdt.c
nasm -f elf gdt.asm -o gdt_asm.o
i586-elf-ld -T link.ld -o kernel32.bin -\( kernel_loader.o main.o stdio.o common.o gdt.o gdt_asm.o -\)

main.o: In function `main':
/cygdrive/c/programming/os/kernel/main.c:12: undefined reference to `puts'
/cygdrive/c/programming/os/kernel/main.c:14: undefined reference to `puts'
/cygdrive/c/programming/os/kernel/main.c:20: undefined reference to `puts'
/cygdrive/c/programming/os/kernel/main.c:22: undefined reference to `puts'
make: *** [kernel32.bin] Error 1

Bearbeiten 3

Wie gewünscht, hier ist die Ausgabe von nm -s auf stdio.o

i586-elf-nm -s stdio.o

00000042 T cls
00000000 D colour
00000000 T init_video
         U memcpy
         U memsetw
0000015e T newline
00000004 D numcols
00000008 D numrows
000001e4 T putch
0000024e T puts
00000004 C screen_mem
000000b8 T scroll
00000291 T set_text_colour
00000016 T strlen
00000199 T writech
00000000 B xpos
00000004 B ypos

Bearbeiten 4
Wie gewünscht, hier sind die gesamten Quelldateien. Ich habe die Dateien in einer ZIP-Datei hochgeladen: http://www.owenstephens.co.uk/media/files/kernel.zip Vielen Dank für das anhaltende Interesse und die Hilfe, das wird sehr geschätzt!

Makefile:

NASM=nasm
GCC=i586-elf-gcc
LD=i586-elf-ld
FMT=-f elf
GFLAGS=-Wall -O0 -fstrength-reduce -fno-inline -nostdinc -nostdlib -fsigned-char -nostartfiles -nodefaultlibs -fno-builtin -fno-stack-protector -I./include -g3 -c 
LFLAGS=-T link.ld
ALL=kernel_loader.o main.o stdio.o common.o gdt.o gdt_asm.o
INCLUDES=include/stdio.h include/common.h include/gdt.h

all: $(ALL) kernel32.bin

kernel_loader.o: kernel_loader.asm
    $(NASM) $(FMT) $*.asm -o $@

main.o: main.c
    $(GCC) $(GFLAGS) $<

stdio.o: stdio.c include/stdio.h
    $(GCC) $(GFLAGS) $<

common.o: common.c include/common.h
    $(GCC) $(GFLAGS) $<

gdt.o: gdt.c include/gdt.h
    $(GCC) $(GFLAGS) $<

gdt_asm.o: gdt.asm
    $(NASM) $(FMT) $< -o $@

kernel32.bin: $(ALL) $(INCLUDES)
    $(LD) $(LFLAGS) -o $@ -\( $(ALL) -\)

clean:
    rm -f $(ALL) kernel32.bin

Linkskript:

OUTPUT_FORMAT("binary")
ENTRY(_start)
phys = 0x00100000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata*)
    . = ALIGN(4096);
  }
  .data . : AT(data)
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss . : AT(bss)
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}

Haupt c:

#include <stdio.h>
#include <common.h>
#include <gdt.h>

int main() {        
    gdt_install();

    puts("This is a minimal example...");

    return 0;
}

gemeinsam.c:

#include <common.h>

void *memcpy(void *dst, const void *src, size_t n) { return (void *)0; }

void *memset(void *dst, const char val, size_t n) { return (void *)0; }

void *memsetw(void *dst, const ushort val, size_t n) { return (void *)0; }

void *memseti(void *dst, const int val, size_t n) { return (void *)0; }

stdio.c:

#include <stdio.h>
#include <common.h>

ushort *screen_mem;
int colour = 0x0F;
int xpos = 0, ypos = 0;
int numcols = 80, numrows = 25;

void init_video() {}

size_t strlen(char *str) { return 0; }

void cls() { }

inline void scroll() { }

inline void newline() { }

void writech(char c) { }

void putch(char c) { }

void puts(char *str) { }

void set_text_colour(unsigned char f, unsigned char b){ }

gdt.c:

#include <gdt.h>

struct gdt_entry gdt[3];
struct gdt_ptr gdtp;

void gdt_set_gate(int n, ulong base, ulong limit, uchar access, uchar gran) { }

void gdt_install() { }

gdt.asm:

global gdt_reset

gdt_reset:
    ret
gdt_reset2:
    ret

include/common.h:

#ifndef __COMMON_H
#define __COMMON_H

typedef unsigned short  ushort;
typedef unsigned char   uchar;
typedef unsigned int    uint;
typedef unsigned long   ulong;
typedef int size_t;

void *memcpy(void *dst, const void *src, size_t n);
void *memset(void *dst, const char val, size_t n);
void *memsetw(void *dst, const ushort val, size_t n);
void *memseti(void *dst, const int val, size_t n);
#endif

include/stdio.h:

#ifndef __STDIO_H
#define __STDIO_H

#include <common.h>

void cls();
void writech(char c);
void putch(char c);
void puts(char *str);
void set_text_colour(uchar f, uchar b);
void init_video();
size_t strlen(char *str);

#endif

include/gdt.h:

#ifndef __GDT_H
#define __GDT_H

#include <common.h>

struct gdt_entry {
    ushort  limit_low;
    ushort  base_low;
    uchar   base_middle;
    uchar   access;
    uchar   granularity;
    uchar   base_high;
} __attribute__((packed));

struct gdt_ptr {
    ushort limit;
    uint base;
} __attribute__((packed));

void gdt_set_gate(int n, ulong base, ulong limit, uchar access, uchar gran);
void gdt_install();

extern void gdt_reset();

#endif

Ausgabe von “objdump -t”ing einer gemeinsam genutzten Bibliothek, die alle .o-Dateien enthält (außer dem Kernel_loader, daher das undefinierte _start-Symbol.

> i586-elf-objdump -t libos.so.1.0.1

libos.so.1.0.1:     file format elf32-i386

SYMBOL TABLE:
08048080 l    d  .text  00000000 .text
08048162 l    d  .rodata    00000000 .rodata
08049180 l    d  .data  00000000 .data
0804918c l    d  .bss   00000000 .bss
00000000 l    d  .stab  00000000 .stab
00000000 l    d  .stabstr   00000000 .stabstr
00000000 l    d  .comment   00000000 .comment
00000000 l    df *ABS*  00000000 main.c
00000000 l    df *ABS*  00000000 stdio.c
00000000 l    df *ABS*  00000000 common.c
00000000 l    df *ABS*  00000000 gdt.c
00000000 l    df *ABS*  00000000 gdt.asm
08048161 l       .text  00000000 gdt_reset2
08049180 g     O .data  00000004 colour
08048125 g     F .text  00000014 memsetw
0804918c g     O .bss   00000004 xpos
08049188 g     O .data  00000004 numrows
08048158 g     F .text  00000005 gdt_install
08048108 g     F .text  0000000a memcpy
080480ee g     F .text  00000005 puts
08049198 g     O .bss   00000018 gdt
08049194 g     O .bss   00000004 screen_mem
080480e0 g     F .text  0000000e putch
08048144 g     F .text  00000014 gdt_set_gate
00000000         *UND*  00000000 _start
08048160 g       .text  00000000 gdt_reset
080480b4 g     F .text  00000005 init_video
080480c8 g     F .text  00000005 scroll
0804918c g       *ABS*  00000000 __bss_start
08048112 g     F .text  00000013 memset
08048080 g     F .text  00000033 main
080480f3 g     F .text  00000014 set_text_colour
080480cd g     F .text  00000005 newline
08049190 g     O .bss   00000004 ypos
080491b0 g     O .bss   00000006 gdtp
0804918c g       *ABS*  00000000 _edata
080491b8 g       *ABS*  00000000 _end
080480c3 g     F .text  00000005 cls
080480b9 g     F .text  0000000a strlen
08048139 g     F .text  0000000a memseti
08049184 g     O .data  00000004 numcols
080480d2 g     F .text  0000000e writech

  • Ich bin vor einiger Zeit einmal darauf gestoßen. Ich denke, es hatte etwas mit meiner libc.a-Datei zu tun. Sind Sie sicher, dass sich Putch und Puts in Ihrer Bibliothek befinden und dass ihre Prototypen in dieser Bibliothek mit Ihrer Verwendung übereinstimmen?

    – nmichaels

    15. Juli 2010 um 17:59 Uhr

  • +1 Dies ist eine äußerst gut geschriebene Frage.

    – e.James

    15. Juli 2010 um 18:00 Uhr

  • Ich habe meine eigenen Pseudo-stdio-Funktionen implementiert (puts, putch …), die Funktionen stimmen genau überein – das ist das wirklich Seltsame, nur wenn ich den Funktionsaufruf install_gdt hinzufüge, bricht alles zusammen.

    – owst

    15. Juli 2010 um 18:07 Uhr

  • Ich weiß nicht, was das bedeuten könnte oder wohin es gehen könnte, aber Sie sagen, dass “gdt_install in der C-Datei ist” – aber der Dump von main.o zeigt an, dass es undefiniert ist (außerhalb von main.o).

    – Michael Burr

    15. Juli 2010 um 18:09 Uhr

  • “Farbe” ist falsch geschrieben. Stellen Sie sicher, dass Sie die britische Version der Bibliothek haben 🙂

    – Hans Passant

    15. Juli 2010 um 18:44 Uhr

Das klingt nach einem Zirkelbezugsproblem auf der Verbindungslinie. Der Linker geht die Objektdateien der Reihe nach durch und “merkt sich” alle nicht aufgelösten Externals. Es kann jedoch auch alle Objektdateien verwerfen, die keine Verweise auf sie haben. Wenn zwei oder mehr Objektdateien aufeinander verweisen (was einen Zirkelverweis verursacht), ist der Linker möglicherweise nicht in der Lage, die nicht aufgelösten Entitäten zu verfolgen.

Versuchen Sie, Teile der Verbindungslinie zu duplizieren, und grenzen Sie sie dann auf das ein, was Sie benötigen.

i586-elf-ld -T link.ld -o kernel32.bin kernel_loader.o main.o stdio.o common.o gdt.o gdt_asm.o \
        stdio.o common.o gdt.o gdt_asm.o

  • Dies scheint zu “mehreren Definitionen von “-Fehlern und mehr undefinierten Verweisen auf verschiedene Symbole zu führen, also kein Glück!

    – owst

    19. Juli 2010 um 17:32 Uhr

Die Reihenfolge der Dateien auf der Befehlszeile scheint beim GNU-Linker von Bedeutung zu sein. Lege das .o Datei mit dem Einstiegspunkt (kernel_loader.o) zuerst in der Befehlszeile, dann alle Objekte, auf die es direkt verweist, dann die Objekte, auf die von diesen Objekten verwiesen wird (und die sich nicht bereits in der Befehlszeile befinden) usw., oder es ist möglich, dass der Linker einige Dateien vermisst.

  • Aha! Ich hatte gehofft, jemand anderes würde dies vorschlagen, ich hatte gedacht, dass ich das Problem auf diese Weise behoben hätte. Ich weiß nicht, ob es sich um eine Art Zirkelverweis handelt, aber nach dem Neuanordnen würde ich in die Situation geraten, in der das Verschieben / Aufrufen von Funktionen dazu führte, dass der Link erneut fehlschlug …

    – owst

    15. Juli 2010 um 18:55 Uhr

  • Ja, das scheint zu funktionieren. Wenn ich die Objektdateien in der Reihenfolge ihrer Einbindung anordne (und dann ihre Funktionen aufgerufen werden), scheint es zu funktionieren … Wenn ich jedoch den problematischen gdt_install-Aufruf an den Anfang der Quelldatei main.c verschiebe, erhalte ich undefiniert Verweise.

    – owst

    15. Juli 2010 um 18:58 Uhr

  • Versuchen Sie, die Objektdateien (außer dem Einstiegspunkt) mit (rm -f libfoo.a ; ar -cr libfoo.a a.o b.o c.o ; ranlib libfoo.a), dann gegen verlinken libfoo.a anstelle der einzelnen Objektdateien.

    – Aidan Cully

    15. Juli 2010 um 18:59 Uhr

  • Argh, nein! Es schlägt immer noch fehl und behauptet, wie zuvor, eine undefinierte Referenz.

    – owst

    15. Juli 2010 um 19:14 Uhr

  • Versuchen Sie, die Objektdateiliste in -Wl,-( und -Wl,-) einzuschließen (möglicherweise müssen Sie die Klammern mit einem Backslash-Escapezeichen versehen).

    – zol

    15. Juli 2010 um 23:05 Uhr

Benutzer-Avatar
e.James

Es gibt eine andere SO-Frage (möglicherweise ähnlich / identisch, ich bin mir nicht sicher), die einiges davon abdeckt. Bietet der eine Hilfestellung?

  • Das sieht so aus, als ob der Code aus einem ähnlichen (wenn nicht demselben) Tutorial stammt. Ich werde es überprüfen, danke. Ein Unterschied ist, dass sein Fehler zur Laufzeit auftritt, während meiner zur Verbindungszeit ist … aber wir werden sehen 🙂

    – owst

    15. Juli 2010 um 18:09 Uhr

Ich habe ähnliche Probleme ein paar Mal gesehen und an einem bestimmten Punkt, kurz bevor ich völlig verrückt werde, beginne ich, nach unsichtbaren Dingen zu suchen, die in den Namen enden könnten. Nicht-ASCII-Bytes oder nicht druckbare ASCII-Zeichen, die sich in Ihren Quellcode eingeschlichen und an den ersten Code angehängt haben könnten, der danach angezeigt wird gdt_install();

Vielleicht möchten Sie versuchen, einen Kommentar oder ein leeres Makro (oder eine do{}while(0) ) zwischen Ihrem Anruf an gdt_install() und die nächste echte Codezeile. Gehen Sie vielleicht sogar so weit, den Cursor in den Funktionsnamen zu setzen und ihn bis kurz vor das erste Zeichen dieses Funktionsnamens zurückzusetzen, und beginnen Sie mit der Eingabe dessen, was Sie dort hinzufügen möchten. Wenn es etwas ist, das durch das Vorhandensein von verursacht wird gdt_install(); dann sollte etwas anderes, das dort hineingeworfen wird, einen anderen Fehler erzwingen.

Falls Sie dies noch nicht getan haben, möchten Sie vielleicht die Präprozessorausgabe der Datei mit dem Aufruf von anzeigen gdt_install() sowie seine Montageleistung.

Wenn nichts davon zu etwas Interessantem führt, ändern Sie den Namen von gdt_install und schau ob sich dadurch was ändert. Ich habe einige Fälle gesehen, in denen Fehler im Compiler und/oder Linker so etwas Seltsames erzeugen konnten. Vielleicht ein Fehler in einer Hash-Tabelle, die als Symboltabelle verwendet wird (vielleicht sogar in der Hash-Tabelle der elf-Datei).

Ich hoffe, Sie finden das heraus.

einige wilde Vermutungen, vielleicht Ihre Assembler-Funktion, die aufgerufen (und wahrscheinlich inline) wird gdt_install vermasselt, was danach kommt. (dieser Sprung am Ende vor dem ret ist ungewöhnlich, noch nie gesehen)

Ist dein gdt_install in der gleichen Kompilationseinheit wie wo du den Aufruf hast? Benutzt du -O[12] zum zusammenstellen? Ist der Aufruf inline? Wie sieht der Assembler aus, den der Compiler für die Call-Seite produziert?

Hast du versucht mit zu kompilieren -O0 -g oder -fno-inline (oder wie heißt diese Option)?

  • Der Sprung wird verwendet, um den korrekten CS-Registerwert (den Codedeskriptor) zu “installieren”. Ich glaube nicht, dass die Assembler-Funktion inline sein könnte, da sie separat kompiliert wird (mit Nasm), obwohl ich mich durchaus irren könnte. Ich habe bereits alle Optimierungen und Inlining deaktiviert.

    – owst

    20. Juli 2010 um 22:22 Uhr

Benutzer-Avatar
JayM

Sie schrieben. “I've implemented my own psuedo-stdio functions (puts, putch...) the functions match exactly ...” Verwenden Sie irgendetwas in der Standard-Libc? Wenn nicht, sollten Sie hinzufügen -nostdlib zu Ihrer Befehlszeile. Ich habe seltsame Dinge gesehen, als ich versucht habe, Funktionen in der Standard-Libc zu überschreiben.

  • Der Sprung wird verwendet, um den korrekten CS-Registerwert (den Codedeskriptor) zu “installieren”. Ich glaube nicht, dass die Assembler-Funktion inline sein könnte, da sie separat kompiliert wird (mit Nasm), obwohl ich mich durchaus irren könnte. Ich habe bereits alle Optimierungen und Inlining deaktiviert.

    – owst

    20. Juli 2010 um 22:22 Uhr

Benutzer-Avatar
Nategans

Ich vermute, dass vielleicht das Kombinieren der Verknüpfungssymboltabellen aus dem von Nasm generierten Objekt und den von gcc/gas generierten Objekten etwas durcheinander bringen könnte.

Könnten Sie versuchen, den Anruf zu ersetzen gtd_install mit einem Aufruf einer kurzen Inline-Funktion, die eine Inline-Assembly enthält, die aufruft oder dorthin springt gtd_install und befindet sich in derselben Datei wie der aktuelle Aufruf gtd_install?

Eine andere Sache, die mir gerade in den Sinn gekommen ist, ist, dass if gtd_install in Assembler geschrieben ist, ist es möglich, dass es syntaktisch nicht 100% korrekt ist. Ich habe so etwas noch nie gesehen, aber ich denke nur, dass es möglich sein könnte, dass gtd_installDie Grenzen von (insbesondere das Ende) oder seine Größe werden vom Assembler nicht korrekt bestimmt, und das führt nur zu zufälligen Ergebnissen.

Abgesehen davon denke ich, dass Sie zu den Binutils-Leuten gehen und sie direkt um Hilfe bitten müssen.

1355310cookie-checkWie kann das Hinzufügen eines Funktionsaufrufs dazu führen, dass andere Symbole beim Verknüpfen undefiniert werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy