2016-07-12 175 views
10

Sto provando a compilare un sorgente con tcc (ver 0.9.26) contro un file .o generato da gcc, ma ha un comportamento strano. Gcc (ver 5.3.0) è di MinGW 64 bit.comportamento strano quando si tenta di compilare un sorgente con tcc contro gcc generato .o file

In particolare, ho i seguenti due file (te1.c te2.c). Ho i seguenti comandi sulla scatola 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!!! 

noti che tagliato 4 byte dalla stringa 1234567in dummy!!!\n. Mi chiedo se cosa potrebbe essere andato storto.

Grazie Jin

======== file di te1.c ===========

#include <stdio.h> 

void dummy() { 
    printf1("1234567in dummy!!!\n"); 
} 

file di ======== 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; 
} 

Aggiornamento 1

Saw una differenza di assemb tra te1.o (te1.c compilato da tcc) e te1_gcc.o (te1.c compilato da gcc). Nel tcc compilato, ho visto lea -0x4(%rip),%rcx, sul gcc compilato, ho visto lea 0x0(%rip),%rcx. Non so perché.

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

Utilizzando un editor binario, ho cambiato il codice macchina in te1.o (prodotto da gcc) e cambiato lea 0(%rip),%rcx-lea -0x4(%rip),%rcx e utilizzando il TCC di collegarlo, le opere exe portato bene. Più precisamente, ho fatto

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

Update 3

Come richiesto, ecco l'output del 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

È probabile che 'tcc' e' gcc' abbiano convenzioni di chiamata predefinite diverse. Potrebbe volerlo controllare. –

+1

Se 'te1.c' ha' extern void printf1 (char * p); 'o include un'intestazione che dichiara' printf() '? – RastaJedi

+1

Problema collaterale: 'extern void dummy();' dovrebbe essere 'extern void dummy (void);', altrimenti 'main()' potrebbe chiamare 'dummy (1,2,3)' senza avviso/errore. – chux

risposta

5

aggiungere

extern void printf1(char *p); 

al tuo te1.c file

Oppure: il compilatore assume argomento numero intero a 32 bit poiché non esiste un prototipo ei puntatori sono lunghi 64 bit.

Modifica: questo non funziona ancora. Ho scoperto che la funzione non ritorna mai (poiché chiamare il printf1 una seconda volta non fa nulla!). Sembra che i primi 4 byte siano consumati come indirizzo di ritorno o qualcosa del genere. Nella modalità gcc a 32 bit funziona perfettamente. Mi sembra un problema con le convenzioni di chiamata ma non riesco ancora a capirlo. Un altro indizio: chiamare il printf dal lato te1.c (gcc, usando i collegamenti tcc stdlib) si blocca con segv.

Ho disassemblato l'eseguibile.La prima parte viene ripetuta dal lato 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 

La seconda parte viene ripetuta (6 volte) chiamata alla stessa funzione. Come puoi vedere l'indirizzo è diverso (spostato di 4 byte, come i tuoi dati) !!! E 'sorta di funziona solo una volta perché le prime 4 istruzioni sono le seguenti:

401000:  55      push %rbp 
401001:  48 89 e5    mov %rsp,%rbp 

così stack viene distrutto se coloro che vengono saltati !!

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 
+0

% c3% a7ois-fabre Grazie per il suggerimento, l'ho provato, ma ho ottenuto lo stesso risultato di prima. – packetie

+1

Posso riprodurre almeno il bug! –

+0

È bello saperlo! Per favore fatemi sapere cosa scoprite. Ho intenzione di aggiungere una taglia per questo :-) – packetie

6

non ha nulla a che fare con tcc o chiamando convenzioni. Ha a che fare con diverse convenzioni di linker per i formati elf64-x86-64 and pe-x86-64.

Con PE, il linker sottrarrà 4 implicitamente per calcolare l'offset finale.

Con ELF, non lo fa. Per questo motivo, 0 è il valore iniziale corretto per PE e -4 è corretto per ELF.

Sfortunatamente, objcopy non converte questo -> bug in objcopy.

+0

Avrebbe molto senso! Potresti fornire qualche link? Principalmente mi interessava "Ha a che fare con diverse convenzioni di linker per i formati elf64-x86-64 e pe-x86-64". – ead

+0

Vedere anche: https://sourceware.org/bugzilla/show_bug.cgi?id=970 - wontfix, vedere: https://sourceware.org/ml/binutils/1999-q3/msg00611.html – h1n1

+0

Ho notato che usando Compilatore a 32 bit + objcopy il bug non c'è. Forse la soluzione è che, mentre si spera che risolva l'objcopy. –