【问题标题】:How can adding a function call cause other symbols to become undefined when linking?添加函数调用如何导致其他符号在链接时变得未定义?
【发布时间】:2011-03-16 13:27:14
【问题描述】:

我希望有人能够帮助解决我认为是链接描述文件的问题。

在添加对新函数的调用后,我遇到了一个奇怪的问题。如果没有函数调用,我的目标文件会正确链接,但是,添加新的函数调用后,我会从另一个目标文件中获得对符号的未定义引用(我已经使用 objdump 验证了它实际上存在)。

同样奇怪的是,在函数调用存在的情况下,如果我首先使用 ld -r 链接所有目标文件(以提供可重定位的输出),然后使用我的链接脚本,则没有未定义的引用,但链接脚本似乎是由于输出二进制文件没有正确的入口点而被忽略。

我的(交叉编译器)ld 版本:

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

我试图证明“缺失”符号存在:

> 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'

注意(当我产生这个输出时,我使用的是 gdt.bin 的文件名作为 nasm 编译的汇编器,它只是另一个 .o 文件,真的)

我可以在相应的目标文件中看到“缺失”的符号:

> i586-elf-objdump -ht stdio.o
stdio.o:文件格式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

以及未解析引用的目标文件:

> 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

我的链接脚本(不确定是否相关):

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 = .;
}

如果我在 main.c 中注释掉对 putch 的调用,则会得到对 puts 的未定义引用...如果我删除对 gdt_install 的调用,则不会出错!

gdt_install 在 C 文件中,但 gdt_install 调用了一个在 gdt.asm 中定义的函数。

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

为了尝试进一步诊断原因,我一直在尝试重新创建错误。如果我将 gdt_install() 函数调用移动到源代码中的特定位置,我不会收到任何错误并且一切正常:

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;
}

如果我将调用移到第一个 puts() 调用之上,我会收到未定义的 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'

接下来,如果我将调用移到 putch() 之上,它会导致对 putch 的未定义引用(这是我最初调用的位置):

...
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'

最后,在 init_video() 之上,导致对 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'

到底是什么导致了这个错误?就像 gdt_install 调用以某种方式“破坏”其他符号......我在任何文档中都找不到对它的任何引用,但是 gdt_install 函数调用是否有某种方式可能导致某些链接器“边界”被溢出,破坏其他代码?

有没有人遇到过这样的问题,或者对进一步调查有什么想法?我在 osdev 论坛上发过帖子:http://forum.osdev.org/viewtopic.php?f=1&t=22227,但运气不佳。

谢谢

编辑:

我不确定它是否相关,但如果我在链接时省略了链接脚本,所有以前的错误都会消失......(虽然,我的引导加载程序无法调用内核,因为它不理解 elf 二进制文件)。

根据要求,这里是预处理前后的main.c文件,并从编译后的main.o文件中反汇编出来。

预处理前:

#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;
}

预处理后:

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;
}

再次编辑:

感谢 nategoose 建议 -g3 提供更好的反汇编输出:

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    

现在是干净制作的新输出:

$ 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

编辑 3

根据要求,这是 stdio.o 上 nm -s 的输出

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

编辑 4 根据要求,这里是整个源文件。我已将文件以 zip 格式上传至:http://www.owenstephens.co.uk/media/files/kernel.zip 感谢您一直以来的关注和帮助,非常感谢!

生成文件:

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

链接脚本:

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 = .;
}

main.c:

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

int main() {        
    gdt_install();

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

    return 0;
}

common.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

包括/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

包括/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

包括/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

“objdump -t”的输出包含所有 .o 文件的共享库(kernel_loader 除外,因此是未定义的 _start 符号。

> 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

【问题讨论】:

  • 我确实遇到过一次,不久前。我认为这与我的 libc.a 文件有关。您确定 putch 和 puts 在您的库中,并且它们在该库中的原型与您对它们的使用相匹配吗?
  • +1 这是一个写得非常好的问题。
  • 我已经实现了我自己的 psuedo-stdio 函数(puts、putch...),这些函数完全匹配 - 这真的很奇怪,只有当我添加 install_gdt 函数调用时它才会全部中断下来。
  • 不知道这可能意味着什么或它可能会去哪里,但你说“gdt_install 在 C 文件中” - 但是 main.o 的转储表明它是未定义的(在 main.o 之外)。 o)。
  • "color" 拼写错误。确保你有英国版的图书馆 :)

标签: c assembly linker compilation binutils


【解决方案1】:

这听起来像是链接行上的循环引用问题。链接器按顺序遍历目标文件并“记住”任何未解析的外部文件。但是,它也可以丢弃任何没有引用它们的目标文件。如果两个或多个目标文件相互引用(导致循环引用),链接器可能无法跟踪未解析的实体。

尝试复制链接线的一部分,然后将其缩小到您需要的范围。

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

【讨论】:

  • 这似乎会导致“ 的多重定义”错误以及对不同符号的更多未定义引用,所以运气不好!
【解决方案2】:

命令行上文件的顺序似乎与 GNU 链接器有关。将包含入口点 (kernel_loader.o) 的 .o 文件首先放在命令行上,然后是它直接引用的任何对象,然后是这些对象引用的对象(并且这些对象还没有在命令行上),等等,或者链接器可能会丢失一些文件。

【讨论】:

  • 啊哈!我希望其他人会提出这个建议,我以为我已经通过这种方式解决了这个问题。我不知道这是否是某种循环引用样式的问题,但是在重新排列之后,我会遇到移动/调用函数导致链接再次失败的情况......
  • 是的,这似乎有效。如果我按照包含的顺序(然后调用它们的函数)对目标文件进行排序,它似乎可以工作......但是,当我将有问题的 gdt_install 调用移动到 main.c 源文件的顶部时,我得到 undefined参考文献。
  • 尝试将目标文件(入口点除外)放入带有 (rm -f libfoo.a ; ar -cr libfoo.a a.o b.o c.o ; ranlib libfoo.a) 的存档中,然后链接 libfoo.a 而不是单个目标文件。
  • 啊,不!它仍然错误,声称未定义的引用,和以前一样。
  • 尝试将目标文件列表包装在 -Wl,-( 和 -Wl,-) 中(您可能需要用反斜杠转义括号)。
【解决方案3】:

another SO question(可能相似/相同,我不确定)涵盖了其中的一些内容。那个有帮助吗?

【讨论】:

  • 看起来代码来自类似(如果不相同)教程。我去看看,谢谢一个区别是他的错误是在运行时,而我的错误是链接时......但我们会看到:)
【解决方案4】:

我见过几次类似的问题,在某个时刻,就在我完全发疯之前,我开始寻找可能最终出现在名称中的无形的东西。非 ASCII 字节或不可打印的 ASCII 字符可能已潜入您的源代码并附加到gdt_install(); 之后看到的第一个代码

您可能想尝试在对 gdt_install() 的调用和下一个实际代码行之间添加注释或空宏(或 do{}while(0) )。甚至可以将光标放在函数名称中并将其备份到该函数名称的第一个字符之前,然后开始输入您决定添加的任何内容。如果这是由gdt_install(); 的存在引起的,那么其他东西被扔进去应该会导致另一个错误。

如果您还没有,您可能希望通过调用gdt_install() 来查看文件的预处理器输出及其汇编输出。

如果这些都没有产生任何有趣的东西,请更改gdt_install 的名称,看看是否有任何改变。我见过一些例子,编译器和/或链接器中的错误可能会产生像这样奇怪的东西。可能是用作符号表的哈希表中的错误(甚至可能在 elf 文件的哈希表中)。

希望你能解决这个问题。

【讨论】:

  • 关于非ASCII字符的好主意;不幸的是,我什么也没发现。更改名称并没有导致不同的行为。我将添加(在编辑中)带有问题调用的文件的预处理器和程序集输出。感谢您的想法!
  • gcc 的汇编输出在标签方面可能更具可读性。
  • @owst:尝试将-g3 添加到您的编译命令中,看看链接器错误是否有任何变化。 ld 使用调试符号来生成与源代码更相关的错误。您始终可以在构建后剥离二进制文件。此外,如果您还没有尝试使用 -O0 构建,则仅用于错误输出。
  • 链接器错误现在更具描述性(现在使用文件名/行而不是偏移量),但它们似乎显示相同的错误消息。我使用的是 -O,我认为它与 -O0 相同,但我也尝试过,以防万一。生成的输出似乎没有太大区别...
  • @owst: -O-O1 相同。
【解决方案5】:

一些疯狂的猜测,也许你在gdt_install 中调用(并且可能内联)的汇编函数正在搞乱它之后的内容。 (ret 之前最后的这种跳转是不寻常的,从未见过)

您的gdt_install 与您拨打电话的位置在同一个编译单元中吗?您是否使用-O[12] 进行编译?调用是内联的吗?编译器为调用端生成的汇编器是什么样子的?

您是否尝试使用-O0 -g-fno-inline(或如何调用此选项)进行编译?

【讨论】:

  • 跳转用于“安装”正确的 CS 寄存器值(代码描述符)。我不认为汇编函数可以被内联,因为它是单独编译的(使用 Nasm),尽管我很可能是错的。我已经禁用了所有优化和内联。
【解决方案6】:

你写的。 "I've implemented my own psuedo-stdio functions (puts, putch...) the functions match exactly ..." 你在使用标准 libc 中的任何东西吗?如果不是,则应将-nostdlib 添加到命令行。当我尝试覆盖标准 libc 中的函数时,我看到发生了奇怪的事情。

【讨论】:

  • 不,我没有使用 std libc 中的任何东西;我已经拿到那面旗帜了。不过谢谢!
【解决方案7】:

我怀疑来自 nasm 生成对象和 gcc/gas 生成对象的链接符号表的组合可能会搞砸一些事情。

您是否可以尝试将对gtd_install 的调用替换为对包含内联汇编的短内联函数的调用,该内联汇编调用或跳转到gtd_install 并与当前对gtd_install 的调用位于同一文件中?

我想到的另一件事是,如果gtd_install 是用汇编语言编写的,那么它的语法可能不是 100% 正确的。我从未见过这样的事情,但只是认为gtd_install 的边界(尤其是结尾)或其大小可能不是由汇编程序正确确定的,而这只是随机产生的结果。

我认为您将不得不去找 binutils 人员并直接向他们寻求帮助。

【讨论】:

    猜你喜欢
    • 2019-02-15
    • 2019-06-10
    • 2017-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-01
    • 1970-01-01
    • 2023-03-19
    相关资源
    最近更新 更多