[geloest] cl.exe [VS2012] generiert falschen Sprungcode



  • Hi,
    ich wollte mal testweise einen kleinen Kernel in Visual C++ schreiben. Man kann sich streiten, ob dieser Compiler fuer sowas geeignet ist, aber das soll hier nicht das Thema sein.
    Jedenfalls scheint der cl fehlerhaften Sprungcode zu generieren. Gelinkt wird das ganze mittels LD, aber eigentlich muesste er die Symbole ja trotzdem korrekt anspringen. Aber es klappt weder ein 'normaler' aufruf mittels foo(bar); , noch gingen jmp/call-Instruktionen im inline Assembler.
    Hier der betreffende Code:

    #include "Types.h"
    #include "MBInfo.h"
    
    __declspec(noinline)
    byte* printCmdLine(const Multiboot::Info& mbi, byte* vidmem)
    {
    	ccstr commandLine = "No cmdLine found!"; //Workaround (mbi.cmdLine zeigt ins Blaue)
    
    	while(*commandLine) { *vidmem++ = *commandLine++; ++vidmem; };
    
    	return vidmem;
    }
    
    extern "C"
    __declspec(noreturn)
    void kmain(dword mbMagic, Multiboot::Info* mbInfo)
    {
    	Multiboot::Info bootInfo = *mbInfo;
    
    	ccstr prnt = "Booted successfully \\o/    ";
    	byte* vidmem = reinterpret_cast<byte*>(0xb8000);
    	while(*prnt) { *vidmem++ = *prnt++; ++vidmem; };
    
    	vidmem = printCmdLine(bootInfo, vidmem);
    
    	__asm cli;
    	__asm hlt;
    }
    

    Urspruenglich war auch der Multiboot-Entry als Funktion definert, die kmain aufruft, aber der ist irgendwo in die Mitte (jedenfalls weit hinter push esp & co.) gesprungen, weshalb ich das in ein extra Assembler-File auslagern musste.
    Hier springt er beim Aufruf von printCmdLine auch irgendwo ins Nirwana, jedenfalls triggert der Bochs-Breakpoint an der Adresse, an der laut Linker-Map die adresse steht, nicht, und die Ausgabe wird auch nicht angezeigt. Falls die Optionen was nuetzen, hier die Flags fuer die Kommandos aus dem Makefile:

    ASM = nasm
    ASMFLAGS = -f elf32 #coff not working for some reason
    CL = cl.exe
    CLFLAGS = /c /nologo /X /W4 /GF /Gm- /GR- /Zp4 /GS- /Zl /volatile:iso /kernel /Gd
    LD = i586-elf-ld
    LDFLAGS = -nostdlib -Map Kernel.map -o kernel.sys -T kernel.ld
    

    Im wesentlichen sind ja nur Exceptions deaktiviert und einige Laufzeitchecks ausgeschaltet, aber kann ja sein, dass eine Option da mit reinspielt.

    Danke schonmal.



  • Was sagt denn ein Disassembler dazu?



  • Nach etwas IDA Pro Free (die Demo mag wohl keine reinen Binaries 👎 ) Voodoo hat sich herausgestellt, dass es Debug-Code war, den VS produziert hat, der aber in einer eigenen Section lag, statt in .text. Da ich diese nicht im Linker-Script angegeben habe, hat ld sie einfach irgendwo hinter das BSS-Segment gestellt. Dieser Teil wurde aber vom Loader nicht mehr erfasst, da der Multiboot-Header nur .text, .rdata und .data zwischen loadBegin und loadEnd angegeben hatte. Ergebnis: Der Code ist in nicht geladene, und damit mit Muell befuellte, Regionen gesprungen.
    Nach einer Umstellung des Linker-Scripts springt er nun in validen Code. Trotzdem bricht er ab, weil der Debug-Code via int 0x01 eine Software Exception triggert -- bei deaktivierten Interrupts und ungesetzter IDT.
    Weiss irgendjemand, wie man VS diese Code-Generation austreibt? Momentane Compiler-Flags:

    CLFLAGS = /c /DNDEBUG /Oy- /nologo /kernel /X /W4 /GF /Gd /GR- /Gy- /Gm- /Zp4 /Zl /J /GL- /GS-
    

    Falls es hilft, kann ich auch noch ein Disassembly der Debug-Sections hier mit her kopieren.



  • Nach langem suchen, Disassemblieren und COFF-Specs lesen, kristallisiert sich nun heraus, dass Microsofts COFF sich ziemlich stark von den anderen COFF-Formaten unterscheidet. Ausser dem _bootstrap in NASM und dem _kmain-Symbol, das vom Bootstrap aus angesprungen wird, hat der Linker kein einziges anderes Symbol verknuepfen koennen. Entweder hat er irgendwelchen Schrott aus der .debug$S-Section in Calls eingebaut oder sie komplett leer gelassen (call .+5 bzw. E8 00 00 00 00).

    EDIT: Mit einer neucompilierten Crosstools-Version, die MinGW-Support einkompiliert hat, funktioniert es jetzt. Ich musste zwar das Format beim Linken explizit angeben, da der Linker nicht automatisch zwischen normalen COFF (coff-i386) und Microsoft COFF/PE (pe-i386) unterscheiden konnte, aber das ist ja kein Beinbruch. In jedem Fall funktionierts. Nochmal danke an MrX, dass du mich ueberhaupt auf die Idee gebracht hast, mal mit IDA drueber zu laufen.


Log in to reply