linux - জিএনইউ এসেম্বলার ব্যবহার করে x86_64 এ প্রিন্টফে কল করা




gcc assembly (2)

আমি জিএনইউ এসেম্বলারের সাথে ব্যবহারের জন্য এটি অ্যান্ড টি সিনট্যাক্স ব্যবহার করে একটি প্রোগ্রাম লিখেছি:

            .data
format:   .ascii "%d\n"  

            .text
            .global main  
main:
            mov    $format, %rbx
            mov    (%rbx), %rdi
            mov    $1, %rsi
            call     printf
            ret

আমি জিসিসি একত্রিত করতে এবং এর সাথে লিঙ্ক করতে ব্যবহার করি:

gcc -o main main.s

আমি এই আদেশ দিয়ে এটি চালাতে:

./main

আমি প্রোগ্রামটি চালানোর সময় আমি একটি সেগ ত্রুটি পাই। জিডিবি ব্যবহার করে এটি বলছে যে printf পাওয়া যায়নি। আমি "। এক্সটার্ন প্রিন্টফ" চেষ্টা করেছি, যা কাজ করে না। কেউ পরামর্শ দিয়েছে আমার প্রিন্টএফ কল করার আগে স্ট্যাক পয়েন্টারটি সঞ্চয় করা উচিত এবং আরইটি এর আগে পুনরুদ্ধার করা উচিত, আমি কীভাবে এটি করব?


আপনি সমতুল্য সি ফাইল থেকে উত্পন্ন সমাবেশ কোডটি দেখতে পারেন।
gcc -o - -S -fno-asynchronous-unwind-tables test.c সহ টেস্ট.সি gcc -o - -S -fno-asynchronous-unwind-tables test.c Run

#include <stdio.h>
int main() {
   return printf("%d\n", 1);
}

এই আউটপুট সমাবেশ কোড:

        .file   "test.c"
        .section        .rodata
.LC0:
        .string "%d\n"
        .text
        .globl  main
        .type   main, @function
main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $1, %esi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        popq    %rbp
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 6.1.1 20160602"
        .section        .note.GNU-stack,"",@progbits

এটি আপনাকে এমন একটি অ্যাসেমব্লিং কোডের কলিং প্রিন্টের একটি নমুনা দেয় যা আপনি তারপরে সংশোধন করতে পারেন।

আপনার কোডের সাথে তুলনা করে আপনার 2 টি জিনিস সংশোধন করা উচিত:

  • % rdi ফর্ম্যাটটিতে নির্দেশ করা উচিত, আপনারা% rbx কে অপ্রয়োজনীয় করা উচিত নয়, এটি মুভ mov $format, %rdi দিয়ে করা যেতে পারে mov $format, %rdi
  • প্রিন্টফের একটি পরিবর্তনশীল সংখ্যক আর্গুমেন্ট রয়েছে, তারপরে আপনার mov $0, %eax যুক্ত করা উচিত

এই পরিবর্তনগুলি প্রয়োগ করা এরকম কিছু দেবে:

    .data
format: .ascii "%d\n"  
.text
    .global main  
main:
  mov  $format, %rdi
  mov  $1, %rsi
  mov  $0, %eax
  call printf
  ret

এবং তারপরে এটি মুদ্রণ চালাচ্ছে:

1


এই কোডটি নিয়ে বেশ কয়েকটি সমস্যা রয়েছে। লিনাক্স দ্বারা ব্যবহৃত এএমডি 64 সিস্টেম ভি এবিআই কলিং কনভেনশনের কয়েকটি জিনিস প্রয়োজন। এটি প্রয়োজন যে কোনও কল করার ঠিক আগে স্ট্যাকটি কমপক্ষে 16 বাইট (বা 32-বাইট) সারিবদ্ধ হওয়া উচিত:

ইনপুট আর্গুমেন্টের ক্ষেত্রের শেষেটি 16-এ সারিবদ্ধ করা হবে (32, যদি __m256 স্ট্যাকের উপর দিয়ে যায়) বাইট সীমানায়।

সি রানটাইম আপনার main ফাংশনটি কল করার পরে স্ট্যাকটি 8 দ্বারা মিস্যালাইনড হয় কারণ রিটার্ন পয়েন্টারটি কল দ্বারা স্ট্যাকের উপরে স্থাপন করা হয়েছিল। ১--বাইট সীমানায় পুনরায় স্বাক্ষর করতে আপনি স্ট্যাকের উপর কোনও সাধারণ উদ্দেশ্য নিবন্ধকে ধাক্কা দিতে পারেন এবং শেষে এটি বন্ধ করে দিতে পারেন।

কলিং কনভেনশনটি এও আবশ্যক যে আ'লীগের একটি চলক যুক্তি ফাংশনের জন্য ব্যবহৃত ভেক্টর রেজিস্টারগুলির সংখ্যা থাকতে হবে:

% al ব্যবহার করে একটি ভেরিয়েবলের একটি পরিবর্তনশীল সংখ্যার প্রয়োজন হয় এমন একটি কার্যক্রমে ভেক্টর আর্গুমেন্টগুলির সংখ্যা নির্দেশ করতে ব্যবহৃত হয়

printf একটি পরিবর্তনশীল আর্গুমেন্ট ফাংশন, তাই AL সেট করা দরকার। এই ক্ষেত্রে আপনি কোনও ভেক্টর রেজিস্ট্রারে কোনও প্যারামিটারগুলি পাস করবেন না যাতে আপনি AL এ 0 সেট করতে পারেন।

আপনি যদি ইতিমধ্যে কোনও ঠিকানা হয় তখন $ ফর্ম্যাট পয়েন্টারটিকেও অবহেলা করেন। সুতরাং এটি ভুল:

mov  $format, %rbx
mov  (%rbx), %rdi 

এটি বিন্যাসের ঠিকানা নেয় এবং এটি আরবিএক্সে রাখে । তারপরে আপনি সেই ঠিকানায় 8 বাইটগুলি আরবিএক্স এ নিয়ে সেগুলি আরডিআইতে রাখবেন আরডিআই অক্ষরের একটি স্ট্রিংয়ের পয়েন্টার হওয়া দরকার, অক্ষরগুলি নয় themselves দুটি লাইন এর সাথে প্রতিস্থাপন করা যেতে পারে:

lea  format(%rip), %rdi

এটি আরআইপি সম্পর্কিত সম্পর্কিত ঠিকানা ব্যবহার করে।

আপনার NU টি আপনার স্ট্রিংও বন্ধ করতে হবে। .ascii ব্যবহার .ascii আপনি x86 প্ল্যাটফর্মে .asciz ব্যবহার করতে পারেন।

আপনার প্রোগ্রামের একটি কার্যকরী সংস্করণ এর মতো হতে পারে:

# global data  #
    .data
format: .asciz "%d\n"
.text
    .global main
main:
  push %rbx
  lea  format(%rip), %rdi
  mov  $1, %esi           # Writing to ESI zero extends to RSI.
  xor %eax, %eax          # Zeroing EAX is efficient way to clear AL.
  call printf
  pop %rbx
  ret

অন্যান্য প্রস্তাবনা / পরামর্শ

আপনার the৪-বিট লিনাক্স এবিআই থেকেও সচেতন হওয়া উচিত, যে কলিং কনভেনশনটির জন্য নির্দিষ্ট রেজিস্টার সংরক্ষণের সম্মানের জন্য আপনার লেখা ফাংশনগুলিরও প্রয়োজন। নিবন্ধগুলির তালিকা এবং সেগুলি সংরক্ষণ করা উচিত কিনা তা নিম্নরূপ:

যে কোনও রেজিস্টার যা রেজিস্টার কলাম জুড়ে সংরক্ষিত আছে তা Yes আপনাকে অবশ্যই নিশ্চিত করতে হবে যে আপনার ফাংশন জুড়ে সংরক্ষিত আছে। ফাংশন main অন্য কোনও সি ফাংশনের মতো।

আপনার যদি জানা থাকে যে স্ট্রিং / ডেটা কেবলমাত্র পঠিত হবে তবে আপনি সেগুলি .section .rodata পরিবর্তে .rodata বিভাগে রেখে .section .rodata .data

-৪-বিট মোডে: আপনার কাছে যদি একটি গন্তব্য অপারেন্ড থাকে যা একটি 32-বিট রেজিস্টার, সিপিইউ পুরো 64-বিট রেজিস্টার জুড়ে রেজিস্টারটি প্রসারিত করবে। এটি নির্দেশের এনকোডিংয়ে বাইটগুলি সংরক্ষণ করতে পারে।

আপনার এক্সিকিউটেবলকে পজিশন स्वतंत्र কোড হিসাবে সংকলিত করা সম্ভব। আপনি অনুরূপ একটি ত্রুটি পেতে পারেন:

প্রতীক against প্রিন্টফের বিপরীতে স্থানান্তরিত R_X86_64_PC32 @ ভাগ করা অবজেক্ট তৈরি করার সময় ব্যবহার করা যাবে না; -fPIC এর সাথে পুনরায় কম্পাইল করুন

এটি ঠিক করতে আপনাকে এইভাবে বাহ্যিক ফাংশন printf কল করতে হবে:

call [email protected] 

এটি প্রক্রিয়া লিংকেজ টেবিল (পিএলটি) এর মাধ্যমে বাহ্যিক লাইব্রেরি ফাংশনকে কল করে





gas