❌ About FreshRSS

Normal view

There are new articles available, click to refresh the page.
Today — 8 June 2023News from the Ada programming language world
Yesterday — 7 June 2023News from the Ada programming language world

Segmentation fault in Ada inline assembler [duplicate]

I'm taking my first steps in Ada and attempting to write a package which manipulates the CPUID instruction. I found some sequences on the OSDev wiki here for checking the EFLAGS.ID bit that I'm attempting to modify into an Ada function using inline assembler, but I'm running into a persistent segfault when trying to run it. First, the package spec & body:

-- Specification file: Cpuid.ads --

with Standard_Types; use Standard_Types;

package Cpuid is
    
    function Is_Supported return Boolean;

end Cpuid;

-- Body file: Cpuid.adb --

with System.Machine_Code; use System.Machine_Code;

package body Cpuid is

    function Is_Supported return Boolean is
        HT  : constant Character := Character'Val(16#09#);
        LF  : constant Character := Character'Val(16#0A#);
        EAX : Unsigned_32 := 16#0000_0000#;
    begin
        Asm ("pushfq"                    & LF & HT &
             "pushfq"                    & LF & HT &
             "popq    %%rax"             & LF & HT &
             "xorq    0x00200000, %%rax" & LF & HT &
             "pushq   %%rax"             & LF & HT &
             "popfq"                     & LF & HT &
             "pushfq"                    & LF & HT &
             "popq    %%rax"             & LF & HT &
             "xorq    (%%rsp), %%rax"    & LF & HT &
             "popfq"                     & LF & HT &
             "andq    0x00200000, %%rax" & LF & HT &
             "movl    %%eax, %0",
             Outputs  => Unsigned_32'Asm_Output ("=g", EAX),
             Volatile => True);
        if EAX /= 16#0000_0000# then 
            return True; 
        else 
            return False; 
        end if;
    end Is_Supported;

end Cpuid;

The Cpuid.Is_Supported function is called by a main program which looks like this:

with Ada.Text_IO;    use Ada.Text_IO;
with Standard_Types; use Standard_Types;
with Cpuid;

procedure Cpuid_Check is
    Some_Int : Unsigned_32 := 0;
begin
    if Cpuid.Is_Supported then
        Put_Line ("CPUID instruction supported on this CPU.");
    else
        Put_Line ("CPUID instruction not supported.");
    end if;
end Cpuid_Check;

Note that the Standard_Types import is just a spec-only package consisting of handwritten type definitions for Unsigned_8, Unsigned_16, and Unsigned_32.

When I attempt to run this program, the program fails with PROGRAM_ERROR : EXCEPTION_ACCESS_VIOLATION. Using GDB allows me to trace the error to a SIGSEGV, but I am not able to trace the violation further to a particular instruction issuance. The full debugger output is as follows:

Temporary breakpoint 14, 0x00000000004016e0 in cpuid_check ()
[program stopped: breakpoint-hit]
(gdb) -exec-next
Single stepping until exit from function _ada_cpuid_check,
which has no line number information.
[program running]

Program received signal SIGSEGV, Segmentation fault.
0x0000000000401ede in cpuid.is_supported ()
[program stopped: signal-received]
(gdb) -exec-next
Single stepping until exit from function cpuid__is_supported,
which has no line number information.
[program running]

Program received signal SIGSEGV, Segmentation fault.
0x00007ffc4d3473f9 in KERNEL32!IsBadReadPtr () from C:\Windows\System32\kernel32.dll
[program stopped: signal-received]
(gdb) 

I've checked to ensure that I'm leaving the stack "as I found it", which, to the best of my ability, appears to be the case - the same number of pushes and pops occur within the inline assembly section. I've also rearranged the assembly sequence into src, dest format, as my reading on GAS indicates that is the syntax used. In the process, however, I bamboozled myself - I'm more used to Intel/NASM syntax, and GAS looks like a mess in comparison.

Is there a way to rectify this function so that it functions properly?

EDIT: Here is the disassembly dump from GDB, as noted in the comments:

(gdb) disas
Dump of assembler code for function cpuid__is_supported:
   0x0000000000401ec4 <+0>: push   %rbp
   0x0000000000401ec5 <+1>: mov    %rsp,%rbp
   0x0000000000401ec8 <+4>: sub    $0x10,%rsp
   0x0000000000401ecc <+8>: movb   $0x9,-0x1(%rbp)
   0x0000000000401ed0 <+12>:    movb   $0xa,-0x2(%rbp)
   0x0000000000401ed4 <+16>:    movl   $0x0,-0x8(%rbp)
   0x0000000000401edb <+23>:    pushfq 
   0x0000000000401edc <+24>:    pushfq 
   0x0000000000401edd <+25>:    pop    %rax
=> 0x0000000000401ede <+26>:    xor    0x200000,%rax
   0x0000000000401ee6 <+34>:    push   %rax
   0x0000000000401ee7 <+35>:    popfq  
   0x0000000000401ee8 <+36>:    pushfq 
   0x0000000000401ee9 <+37>:    pop    %rax
   0x0000000000401eea <+38>:    xor    (%rsp),%rax
   0x0000000000401eee <+42>:    popfq  
   0x0000000000401eef <+43>:    and    0x200000,%rax
   0x0000000000401ef7 <+51>:    mov    %eax,%eax
   0x0000000000401ef9 <+53>:    mov    %eax,-0x8(%rbp)
   0x0000000000401efc <+56>:    cmpl   $0x0,-0x8(%rbp)
   0x0000000000401f00 <+60>:    je     0x401f09 <cpuid__is_supported+69>
   0x0000000000401f02 <+62>:    mov    $0x1,%eax
   0x0000000000401f07 <+67>:    jmp    0x401f0e <cpuid__is_supported+74>
   0x0000000000401f09 <+69>:    mov    $0x0,%eax
   0x0000000000401f0e <+74>:    nop
   0x0000000000401f0f <+75>:    nop
   0x0000000000401f10 <+76>:    add    $0x10,%rsp
   0x0000000000401f14 <+80>:    pop    %rbp
   0x0000000000401f15 <+81>:    retq   
   0x0000000000401f16 <+82>:    nop
   0x0000000000401f17 <+83>:    nop
   0x0000000000401f18 <+84>:    nop
   0x0000000000401f19 <+85>:    nop
   0x0000000000401f1a <+86>:    nop
   0x0000000000401f1b <+87>:    nop
   0x0000000000401f1c <+88>:    nop
   0x0000000000401f1d <+89>:    nop
   0x0000000000401f1e <+90>:    nop
   0x0000000000401f1f <+91>:    nop
End of assembler dump.

Ada-based applications in Android?

By: Guille
7 June 2023 at 08:53

Hello,
I have been struggling to make my Ada program run on Android (through a Java native interface), with so far no results. As I understand it, the generic Linux GCC compiler will not make the job, because even if it targets the right architecture (arm64), the compiler toolchain needs to be built using Android “binutils”. Is it like that (or even more complex than that)?
In any case, frustrated by the fact that my SAL generated with gprbuild and GCC will refuse to work (at least with the FSF compiler), I posted my issue on Stack Overflow, and somebody threw the idea of using LLVM instead. So I was wondering if the Gnat LLVM project could help me here.
I started reading about LLVM, and I find it a bit hard to grasp. My current understanding is that I could use it to compile Ada into IR code, that would be then compiled into machine code for an specific architecture, without requiring the installation of a particular cross compiler. Is that right?
Is it theoretically feasible to generate a SAL with a Java interface for Android using this toolchain? And if so, how?
Otherwise, could somebody expose the purpose and/or usages of Gnat LLVM?

Before you react, please know that my understanding of compilers is very limited…
In case you are curious about what I am doing, my project is open source, you can take a look at it here:

3 posts - 2 participants

Read full topic

Before yesterdayNews from the Ada programming language world

Comparing Programming Languages Part 1 Scalar Ranges

 Overview

It is often asserted that all general purpose programming languages can solve the same set of problems. This means that no one programming language has functional advantages over any other programming language.

That assertion is only mostly true, which means it is false.

For example, weakly typed languages can perform and make use of implicit type conversions while strongly typed languages cannot. Strongly typed languages must employ explicit conversions to achieve a similar effect.

The purpose of this article is to begin discussing some of the things that are difficult in one commonly used language and relatively easy in another.

Scalar Ranges

Programming languages derived from Pascal syntax allow scalar types and subtypes to be defined by the programmer, while programming languages derived from C syntax do not allow the programmer to define scalar types or subtypes.

In C++, for example, a class must be declared encapsulating the behavior of a scalar type with a programmer specified range of values. Making a subtype of that class then requires the creation of an inherited class expressing the restrictions distinguishing the subtype.

In C++ enums are encapsulated in a class as illustrated by the following Stack Overflow issue:

How can I implicitly convert an enum to its subset and vice versa in C++?

More precisely, the feature I want is like implicitly convert an enum to its subset enum and vice versa.

The code I wish it working:

enum class Human {

    A = 1,

    B = 2,

};

 

enum class Male {    // subset of Human

    A = Human::A,

};

 

enum class Female {    // subset of Human

    B = Human::B,

};

 

 

// some functions can handle all humans

void human_func(Human h) {

    // ...

}

 

// some only take a subset of humans

void male_func(Male m) {

    // ...

}

 

void female_func(Female m) {

    // ...

}

 

 

// and user only uses values of Human as token

constexpr auto SOMEONE = Human::A;

 

int main() {

    human_func(SOMEONE);  // ok

    male_func(SOMEONE);   // also ok, Human::A implicitly converted to Male

    female_func(SOMEONE); // failed, can't convert Human::A to Female.

}

 

But enum cannot do the conversion. Now I have two options:

// 1. static_assert with template parameter

 

template <Human H>

void female_func() {

    static_assert(H == Human::B);

    // ...

}

 

// 2. manually convert it

 

#define _ENUM_TO_ENUM(e1, e2) \

    static_cast<e2>(static_cast<std::underlying_type_t<decltype(e1)>>(e1))

 

void female_func(_ENUM_TO_ENUM(SOMEONE, Female)) {

    // But this way the compiler does not check if the value is valid.

    // I can put anything in.

    // ...

}

 

 

As is shown above, the concept of a scalar range and subrange is complicated by the need in C++ to express such a type as a class.

One answer provided to this question is

enum class Gender {MALE, FEMALE};

 

struct Human

{

    Gender m_gender;

    Human(Gender g) : m_gender{g}

    {}

    virtual ~Human() = default;

};

 

struct Man : public Human

{

    Man() : Human{Gender::MALE}

    {}

};

struct Woman : public Human

{

    Woman() : Human(Gender::FEMALE)

    {}

};

 

void human_func(const Human & h)

{

    //...

}

void man_func(const Man & m)

{

    //...

}

void woman_func(const Woman & w)

{

    //...

}

 

It is clear that this approach may work for an enum with 2 values, but becomes unusable with an enum containing tens or hundreds of values.

The Ada programming language, on the other hand, uses the concept of scalar ranges and subtypes extensively.

The Character type in Ada is an enumeration type with the range of values expressed as nul .. 'ÿ'. The ASCII characters are a subset of the Character type with value in the range of nul .. del. Within the ASCII characters the upper case characters are the range ‘A’ .. ‘Z’ and the lower characters are the range ‘a’ .. ‘z’.

If the programmer wants to pass only upper case characters as a parameter to a procedure the procedure can be defined as

subtype Upper is range (‘A’ .. ‘Z’);

procedure Upper_Action(U : Upper);

This procedure will only accept characters in the range specified by the subtype Upper.

A function that counts all the upper case characters in a string can be defined as

function Count_Uppers (S : in String) return Natural is

   Count : Natural := 0;

begin

   for value of S loop

      if S in Upper then

         Count := Count + 1;

      end if;

    return Count;

end Count_Uppers;

The Ada samples above exhibit the behavior and usage requested by the person using C++ in the Stack Overflow question above.

The Ada program is not encumbered with the heavy syntax and rules associated with C++ classes.

  • 2 June 2023 at 23:23

[ANN] Qplt (Quick Plot)

By: JC001
2 June 2023 at 15:52

Seen on c.l.a:

I have created Qplt (Quick Plot), an Ada-GUI program to quickly produce a plot of a data set, and make it publicly available in hopes that it might prove useful. The program automatically selects axis ranges and tick intervals. The user may select whether points, lines, or both are plotted, and supply a title and axis labels.

Qplt is available at

Enjoy.

10 posts - 4 participants

Read full topic

usb_embedded + RP2040

Hello! I am considering using Ada for my bachelor's thesis since I've had nice experiences before (I've moved to GNAT Studio and everything is alright now).

Specifically, I am implementing a Forth interpreter, which means I (obviously) need to get data from the user and you can guess by the title I am doing that over USB.

https://pico-doc.synack.me/#usb_device_controller points me to the usb_embedded crate and its usb-echo example... the problem is that I can't make it to show anything other than the string I entered as per the example (or see anything in picocom or minicom). I am using Picoprobe + OpenOCD (external, I couldn't figure out how to make it also take -f interface/cmsis-dap.cfg) + gdb from inside GNAT Studio, if that helps.

To cite the example (with the only changes being formatting and the USB stack details:

``` with RP.Device; with RP.Clock; with Pico;

with USB.Device.Serial; with USB.Device; with USB; with HAL; use HAL;

procedure Hello_Pico is Fatal_Error : exception; Max_Packet_Size : constant := 64;

USB_Stack : USB.Device.USB_Device_Stack (Max_Classes => 1); USB_Serial : aliased USB.Device.Serial.Default_Serial_Class (TX_Buffer_Size => Max_Packet_Size, RX_Buffer_Size => Max_Packet_Size);

use type USB.Device.Init_Result; Status : USB.Device.Init_Result;

Message : String (1 .. Max_Packet_Size); Length : HAL.UInt32; begin RP.Clock.Initialize (Pico.XOSC_Frequency);

if not USB_Stack.Register_Class (USB_Serial'Unchecked_Access) then raise Fatal_Error with "Failed to register USB Serial device class"; end if;

Status := USB_Stack.Initialize (Controller => RP.Device.UDC'Access, Manufacturer => USB.To_USB_String ("Something"), Product => USB.To_USB_String ("Here"), Serial_Number => USB.To_USB_String ("1337"), Max_Packet_Size => Max_Packet_Size);

if Status /= USB.Device.Ok then raise Fatal_Error with "USB stack initialization failed: " & Status'Image; end if;

USB_Stack.Start;

loop USB_Stack.Poll;

 if USB_Serial.List_Ctrl_State.DTE_Is_Present then USB_Serial.Read (Message, Length); if Length > 0 then USB_Serial.Write (RP.Device.UDC, Message (1 .. Natural (Length)), Length); end if; end if; 

end loop; end Hello_Pico; ```

I understand the logic behind this, however I don't get where I should do USB_Serial.Write(RP.Device.UDC, "> ", Length);. Nothing is displayed if I add it before the loop, inside it on either part of the USB_Stack.Poll; line OR in either of the ifs. I really feel like I am missing one key part.

Additionally, every (and I mean every) embedded project of any kind on Ada (at least on GitHub, although my Google-fu and DDG-fu showed no results either) has everything BUT an USB example. I would really, really like to avoid C and not give up on this beautiful language, so can someone help me? I really feel like I'm not seeing something so obvious.

Thank you and have a beautiful [insert time of day]!

submitted by /u/uneven-shiver
[link] [comments]
❌
❌