Странное поведение при попытке скомпилировать источник с tcc против gcc файла .o
Я пытаюсь скомпилировать источник с tcc (ver 0.9.26) с gcc -генерированным файлом .o, но он имеет странное поведение. Gcc (ver 5.3.0) - бит MinGW 64.
В частности, у меня есть следующие два файла (te1.c te2.c). Я сделал следующие команды в окне windows7
c:\tcc> gcc -c te1.c
c:\tcc> objcopy -O elf64-x86-64 te1.o #this is needed because te1.o from previous step is in COFF format, tcc only understand ELF format
c:\tcc> tcc te2.c te1.o
c:\tcc> te2.exe
567in dummy!!!
Обратите внимание, что он отключил 4 байта от строки 1234567in dummy!!!\n
. Интересно, что могло пойти не так.
Спасибо
Jin
======== файл te1.c ===========
#include <stdio.h>
void dummy () {
printf1("1234567in dummy!!!\n");
}
======== файл te2.c ===========
#include <stdio.h>
void printf1(char *p) {
printf("%s\n",p);
}
extern void dummy();
int main(int argc, char *argv[]) {
dummy();
return 0;
}
Обновление 1
Увидели разницу в сборке между te1.o(te1.c, скомпилированным tcc) и te1_gcc.o(te1.c скомпилировано gcc). В сборке tcc я увидел lea -0x4(%rip),%rcx
, на скомпилированном gcc я увидел lea 0x0(%rip),%rcx
.
Не знаю, почему.
C:\temp>objdump -d te1.o
te1.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <dummy>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 81 ec 20 00 00 00 sub $0x20,%rsp
b: 48 8d 0d fc ff ff ff lea -0x4(%rip),%rcx # e <dummy+0xe>
12: e8 fc ff ff ff callq 13 <dummy+0x13>
17: c9 leaveq
18: c3 retq
19: 00 00 add %al,(%rax)
1b: 00 01 add %al,(%rcx)
1d: 04 02 add $0x2,%al
1f: 05 04 03 01 50 add $0x50010304,%eax
C:\temp>objdump -d te1_gcc.o
te1_gcc.o: file format pe-x86-64
Disassembly of section .text:
0000000000000000 <dummy>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 20 sub $0x20,%rsp
8: 48 8d 0d 00 00 00 00 lea 0x0(%rip),%rcx # f <dummy+0xf>
f: e8 00 00 00 00 callq 14 <dummy+0x14>
14: 90 nop
15: 48 83 c4 20 add $0x20,%rsp
19: 5d pop %rbp
1a: c3 retq
1b: 90 nop
1c: 90 nop
1d: 90 nop
1e: 90 nop
1f: 90 nop
Update2
Используя двоичный редактор, я изменил машинный код в te1.o(созданный gcc) и изменил lea 0(%rip),%rcx
на lea -0x4(%rip),%rcx
и используя tcc для его соединения, полученный exe работает отлично.
Точнее, я сделал
c:\tcc> gcc -c te1.c
c:\tcc> objcopy -O elf64-x86-64 te1.o
c:\tcc> use a binary editor to the change the bytes from (48 8d 0d 00 00 00 00) to (48 8d 0d fc ff ff ff)
c:\tcc> tcc te2.c te1.o
c:\tcc> te2
1234567in dummy!!!
Обновление 3
В соответствии с запросом, вот вывод objdump -r te1.o
C:\temp>gcc -c te1.c
C:\temp>objdump -r te1.o
te1.o: file format pe-x86-64
RELOCATION RECORDS FOR [.text]:
OFFSET TYPE VALUE
000000000000000b R_X86_64_PC32 .rdata
0000000000000010 R_X86_64_PC32 printf1
RELOCATION RECORDS FOR [.pdata]:
OFFSET TYPE VALUE
0000000000000000 rva32 .text
0000000000000004 rva32 .text
0000000000000008 rva32 .xdata
C:\temp>objdump -d te1.o
te1.o: file format pe-x86-64
Disassembly of section .text:
0000000000000000 <dummy>:
0: 55 push %rbp
1: 48 89 e5 mov %rsp,%rbp
4: 48 83 ec 20 sub $0x20,%rsp
8: 48 8d 0d 00 00 00 00 lea 0x0(%rip),%rcx # f <dummy+0xf>
f: e8 00 00 00 00 callq 14 <dummy+0x14>
14: 90 nop
15: 48 83 c4 20 add $0x20,%rsp
19: 5d pop %rbp
1a: c3 retq
1b: 90 nop
1c: 90 nop
1d: 90 nop
1e: 90 nop
1f: 90 nop
Ответы
Ответ 1
Не имеет никакого отношения к tcc
или вызовам конвенций. Он имеет отношение к различным соглашениям компоновщика для форматов elf64-x86-64 and pe-x86-64
.
С PE, компоновщик будет вычитать 4 неявно для вычисления окончательного смещения.
С ELF это не делает. Из-за этого 0 - это правильное начальное значение для PE, а -4 - для ELF.
К сожалению, objcopy
не преобразует эту ошибку → в objcopy
.
Ответ 2
добавить
extern void printf1(char *p);
в файл te1.c
Или: компилятор будет считать аргумент 32-битным целым числом, поскольку нет прототипа, а указатели - 64-разрядные.
Изменить: это все еще не работает. Я узнал, что функция никогда не возвращается (поскольку вызов printf1 второй раз ничего не делает!). Кажется, что 4 первых байта потребляются как обратный адрес или что-то в этом роде. В gcc 32-битном режиме он работает нормально.
Звучит как проблема вызова вызова для меня, но все еще не может понять это.
Другой ключ: вызов printf
from te1.c
side (gcc, с использованием привязок tcc stdlib) сбой с segv.
Я разобрал исполняемый файл. Первая часть - это повторный вызов со стороны tcc
40104f: 48 8d 05 b3 0f 00 00 lea 0xfb3(%rip),%rax # 0x402009
401056: 48 89 45 f8 mov %rax,-0x8(%rbp)
40105a: 48 8b 4d f8 mov -0x8(%rbp),%rcx
40105e: e8 9d ff ff ff callq 0x401000
401063: 48 8b 4d f8 mov -0x8(%rbp),%rcx
401067: e8 94 ff ff ff callq 0x401000
40106c: 48 8b 4d f8 mov -0x8(%rbp),%rcx
401070: e8 8b ff ff ff callq 0x401000
401075: 48 8b 4d f8 mov -0x8(%rbp),%rcx
401079: e8 82 ff ff ff callq 0x401000
40107e: e8 0d 00 00 00 callq 0x401090
401083: b8 00 00 00 00 mov $0x0,%eax
401088: e9 00 00 00 00 jmpq 0x40108d
40108d: c9 leaveq
40108e: c3 retq
Вторая часть повторяется (6 раз) вызывает ту же функцию. Как вы можете видеть, адрес отличается (сдвиг на 4 байта, как и ваши данные)!!! Это работает как раз один раз, потому что 4 первые инструкции следующие:
401000: 55 push %rbp
401001: 48 89 e5 mov %rsp,%rbp
так что стек уничтожается, если те пропускаются!!
40109f: 48 89 45 f8 mov %rax,-0x8(%rbp)
4010a3: 48 8b 45 f8 mov -0x8(%rbp),%rax
4010a7: 48 89 c1 mov %rax,%rcx
4010aa: e8 55 ff ff ff callq 0x401004
4010af: 48 8b 45 f8 mov -0x8(%rbp),%rax
4010b3: 48 89 c1 mov %rax,%rcx
4010b6: e8 49 ff ff ff callq 0x401004
4010bb: 48 8b 45 f8 mov -0x8(%rbp),%rax
4010bf: 48 89 c1 mov %rax,%rcx
4010c2: e8 3d ff ff ff callq 0x401004
4010c7: 48 8b 45 f8 mov -0x8(%rbp),%rax
4010cb: 48 89 c1 mov %rax,%rcx
4010ce: e8 31 ff ff ff callq 0x401004
4010d3: 48 8b 45 f8 mov -0x8(%rbp),%rax
4010d7: 48 89 c1 mov %rax,%rcx
4010da: e8 25 ff ff ff callq 0x401004
4010df: 48 8b 45 f8 mov -0x8(%rbp),%rax
4010e3: 48 89 c1 mov %rax,%rcx
4010e6: e8 19 ff ff ff callq 0x401004
4010eb: 90 nop