dynamic linking von GAS-Asm zu glibc (64-Bit Linux)



  • Hallo,

    ich möchte von Asm aus auf C-Funktionen zurückgreifen. Ich kann den Asm-Code auch arfolgreich assemblieren und linken - nur beim ausführen bekomme ich eine illegal hardware instruction. GDB gibt folgende Fehlermeldung aus:

    Program received signal SIGILL, Illegal instruction.
    0x00007ffff77eec16 in fprintf () from /lib/libc.so.6
    

    Das GAS-Asm Programm (64-Bit Linux):

    .section .data
      text: .ascii "Hello World!\n\0"
    
    .section .text
    .globl _start
    _start:
      pushq $text
      call printf
      addq $4, %rsp
      pushq $0
      call exit
    

    Assemblieren und Linken:

    as helloworld.s -o helloworld.o
    ld --dynamic-linker /lib/ld-2.12.so `pkg-config --libs glib-2.0` helloworld.o -o helloworld
    

    Kann mir jemand sagen warum das Programm nicht korrekt funktioniert?



  • Was sagt eigentlich file zu folgenden Dateien:

    # file /lib/ld-2.12.so
    # file helloworld.o
    # file helloworld
    # file /lib/ld-linux.so.2
    

    Was passiert, wenn du es wie folgst baust:

    as helloworld.s -o helloworld.o
    ld helloworld.o -o helloworld -lc --dynamic-linker /lib/ld-linux.so.2
    

    PS: Im Code machst du pushq $text, entspricht 8 Bytes auf dem Stack, danach aber addq $4, %rsp, nur 4 Bytes "bereinigt"...



  • Würde mir noch mal Gedanken über die Aufrufkonvention machen - AFAIK wird hier nach der AMD64 ABI verfahren.



  • @abc.w

    # /lib/ld-2.12.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped
    # helloworld.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
    # helloworld: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), not stripped
    # /lib/ld-linux.so.2: symbolic link to `ld-linux-x86-32.so.2'
    

    Das letzte ist interessant, hier sollte doch eigentlich die ld-linux-x86-64.so.2 verwendet werden, oder? Ich hab mal versucht das Programm mit dieser Lib zu linken, dann bekomme ich aber die gleiche Fehlermeldung. Bei deinem geposteten Linkerbefehl erhalte ich diese Fehlermeldung:

    zugriff auf eine fehlerhafte oder defekte Shared Library: ./helloworld
    

    @masm

    Ich hab aber einen Prozessor von Intel. Was müsste ich denn dann anders machen?



  • Ich würde sagen, du hast entweder 32-Bit Linux drauf oder eine 32-Bit CPU drin und versuchst, 64 Bit Code darauf auszuführen - das geht nicht.



  • uname -m ergibt x86_64 also 64-Bit Linux. Ich hab einen Core 2 Duo E8400. Der ist auch 64-Bit.

    Außerdem hab ich es schon geschafft die 64-Bit Register anzusprechen, wie hier bei diesem Beispiel:

    .section .data
      hello: .ascii "Hello World\n"
    
    .section .text
    .globl _start
    _start:
      movq $4,%rax
      movq $1,%rbx
      movq $hello,%rcx
      movq $12,%rdx
      int $0x80
    
      movq $1,%rax
      movq $0,%rbx
      int $0x80
    

    Das würde doch dann auch nicht gehen, oder?



  • Ich hab jetzt mein Programm gegen eine C-Datei gelinkt und dann hab ich es tatsächlich geschafft das Programm auszuführen. 😃 Was beim dynamischen Linken nicht funktioniert hat hab ich aber bisher noch nicht rausgefunden.

    Allerdings ist es mir bisher nur gelungen eine '1' auszugeben, bei einem kompletten String hab ich einen SegFault bekommen.

    Der Code:

    .section .data
      msg: .ascii "Hello World!\n\0"
    
    .section .text
    .globl _prog
    _prog:
      pushq $msg
      call _print_f
      addq $8, %rsp
      ret
    
    #include <stdio.h>
    #include <string.h>
    
    void _prog();
    
    void _print_int(int n){
        printf("%d\n", n);
    }
    
    void _print_f(char *str) {
        fwrite(str, 1, strlen(str), stdout);
    }
    
    int main(){
        _prog();
        return 0;
    }
    

    Wenn ich das pushq $msg durch pushq $1 ersetze und die Funktion _print_int aufrufe, dann erscheint erfolgreich eine 1 auf der Konsole. Bei dem String hingegen meldet mir GDB immer:

    Program received signal SIGSEGV, Segmentation fault.
    0x00007ffff7afd5e2 in strlen () from /lib/libc.so.6
    

    Muss ich da noch irgendetwas beachten, wenn ich einen String ausgeben möchte?



  • Ich musste gerade feststellen, dass das Programm, das einen 1 ausgibt doch nicht korrekt funktioniert. Es wird zwar eine 1 ausgegeben, dies ist aber nicht der Wert, den ich in das Register schreibe. Wenn ich nämlich eine 2 ins Register schreibe, dann wird trotzdem eine 1 ausgegeben. Laut GDB steht der korrekte Wert aber im Register - der Übergabeparameter der Funktion hingegen hat dann aber den falschen Wert 1. Ich versteh jetzt dann gar nicht mehr...



  • Ich würde Tippen, das du hier, wie schon erwähnt, nach der AMD64 ABI verfahren musst. Ich weiß nicht wie die Aufrufkonvention heißt, aber sie ist ähnlich zu M$'s fastcall: die parameter werden durch rdi,rsi,rcx,rdx,r8 und r9 übergeben (floating point durch xmm0-xmm7). Weiter Parameter werden auf den Stack gepusht.
    Am besten du schaust dir mal mit einem Debugger oder Disassmbler den Code vom Compiler an - da siehst du dann wie die libc-Funktionen Aufgerufen werden.



  • Du hattest Recht, masm. In den von dir genannten Register müssen tatsächlich die Übergabewerte stehen. Reihenfolge: rdi, rsi, rdx, rcx, r8, r9. rdi nimmt dabei das erste Argument auf.

    Jetzt läuft (vorerst) alles so wie es soll. Danke euch!


Log in to reply