Я надеюсь, что кто-то сможет помочь устранить проблему, которая, по моему мнению, является проблемой сценария компоновщика.
У меня возникла странная проблема после добавления вызова новой функции. Без вызова функции мои объектные файлы связываются правильно, однако с добавленным вызовом новой функции я получаю неопределенную ссылку на символ из другого объектного файла (я подтвердил, что он действительно присутствует, используя objdump).
Также странно, что с присутствующим вызовом функции, если я сначала свяжу все объектные файлы с помощью ld -r (чтобы получить перемещаемый вывод), а затем используя свой скрипт ссылки, нет неопределенных ссылок, но кажется, что скрипт ссылки игнорируется, так как выходной двоичный файл не имеет правильной точки входа.
Моя (кросс-компиляторная) версия ld:
> i586-elf-ld - версия
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'
N.B. (когда я создавал этот вывод, я использовал имя файла gdt.bin для скомпилированного ассемблера nasm, на самом деле это просто еще один файл .o)
Я вижу символ, который «отсутствует» в соответствующем объектном файле:
> i586-elf-objdump -ht stdio.o
stdio.o: формат файла elf32-i386Sections: 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-эльф-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 = .;
}
Если я закомментирую вызов putch в main.c, я вместо этого получаю неопределенные ссылки на put ... если я удалю вызов 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;
}
Если я перемещаю вызов над первым вызовом put (), я получаю неопределенные ссылки на put !:
...
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:
$ 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
Как и просили, вот вывод nm -s на 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
Редактировать 4 В соответствии с запросом, вот все исходные файлы. Я загрузил файлы в виде zip-архива по адресу: http://www.owenstephens.co.uk/media/files/kernel.zip Спасибо за постоянный интерес и помощь, мы очень ценим это!
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
Скрипт ссылки:
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
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
Вывод команды "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
start
- в каком файле определеноstart
? - person Aidan Cully   schedule 15.07.2010stdio.c
? Или что на выходеi586-elf-nm -s stdio.o
? - person JayM   schedule 21.07.2010objdump -t
смысл в отношении того, что определено, а что нет в этом модуле? - person nategoose   schedule 22.07.2010