跳轉到內容

Pollen 程式語言

25% developed
來自華夏公益教科書



75% developed 關於這本書
75% developed 介紹
50% developed 現代語言中的作業系統開發

C 程式語言

[編輯 | 編輯原始碼]

安全漏洞的主要來源是指標。

  • 沒有指標,就沒有空指標解引用。
  • 沒有指標,就沒有指標運算。
  • 沒有指標運算,就沒有陣列越界。

傳統上,C 原始碼分為 .c 和 .h。C 原始碼不包含指標。標頭檔案包含使用指標的宏。

標頭檔案可用於定義 .c 檔案使用的型別定義。類似於變數宣告,新的識別符號位於 typedef 的右側。以下宣告指向 my_struct 記錄的指標為 my_struct_t。傳統上,_t 表示“型別”,用於將型別識別符號與變數和引數識別符號區分開來。

/* typedef <existing_name> <alias_name>; */
struct my_struct {
    int my_field;
};

typedef struct my_struct *my_struct_t;


Or simply...

typedef struct my_struct {
    int my_field;
} *my_struct_t;

在原始檔上禁止使用符號 "&" 和 "*"。

使用記憶體池在堆上分配新物件。Apache APR 庫提供了記憶體池的良好實現。可以將 API 作為實現記憶體池的參考。

面向物件

[編輯 | 編輯原始碼]

為 C++ 程式語言開發的面向物件的概念也適用於 C 程式語言。可以透過使用用於 getter 和 setter 的宏來實現。

getter 和 setter 在對錶達式使用之前檢查空指標解引用。GNOME 專案中的 GLib 執行時庫是用 C 編寫的,並使用此策略來避免記憶體錯誤。維護該庫的安全至關重要,因為 GLib 執行時庫用於所有 GNOME 專案。以下示例演示了在 C 中使用宏進行面向物件的示例

#define NEW_CODEGEN_PLUGIN(ctx)                          \
    (tmpl_codegen_t) pollen_malloc(ctx, sizeof(struct pollen_codegen_plugin));

#define CODEGEN_SET_CONTEXT(codegen, x)                           \
    ((codegen != NULL)? (codegen->ctx = x) : (abort(), NULL))

#define CODEGEN_GET_CONTEXT(codegen)                              \
    ((codegen != NULL)? (codegen->ctx) : (abort(), NULL))

#define CODEGEN_SET_LIBRARY(codegen, lib)                         \
    ((codegen != NULL)? (codegen->library = lib) : (abort(), NULL))

/* Macro to get the library handle from the plugin context */
#define CODEGEN_GET_LIBRARY(codegen)                              \
    ((codegen != NULL)? (codegen->library) : (abort(), NULL))

在上面的程式碼中,abort() 函式呼叫後的逗號允許將 NULL 作為子表示式 (abort(), NULL) 的結果返回給父表示式,即 ...? ... : .... abort 函式返回 void。

使用未知型別的函式也應該使用宏來執行強制轉換。

/* Macro to call a function with no arguments */
#define POLLEN_CALL_SYMBOL_0(symbol, rc)                   \
    do {                                                   \
        int (*fn_ptr)() = (int (*)()) symbol;              \
        rc = fn_ptr();                                     \
    } while(0);

#define NEW_CODEGEN_PLUGIN(ctx)                          \
    (tmpl_codegen_t) templatizer_malloc(ctx, sizeof(struct pollen_codegen_plugin));

虛擬地址空間

[編輯 | 編輯原始碼]

除了安全程式語言之外,還可以使用虛擬定址。這允許開發人員包含用舊版程式語言編寫的舊版程式碼。

只有 root 使用者可以執行將作為不同使用者執行的程式(setuid 除外)。守護程序可以分叉其程序,以便在非特權使用者下執行程序,以隔離彼此的每個 API 請求。

因此,唯一的主要問題是解析請求輸入(例如 JSON 或 XML)的程式部分,這些程式包含請求的身份驗證資料以及套接字連線本身。處理此類資料的伺服器部分應由用安全語言編寫的經過仔細驗證的框架處理,而包含用舊版程式語言編寫的程式碼的部分應位於分叉或執行的 worker 程序中。

Ada 程式語言

[編輯 | 編輯原始碼]

Ada 是一種專注於記憶體安全以及程式整體正確性的程式語言。存在 SPARK 子集(不是過時的 SPARC 架構),它從數學上證明給定程式沒有缺陷。

美國政府製造的阿里安火箭是用 Ada/SPARK 編寫的,由於軟體缺陷而爆炸。當討論 Ada 作為一種非常安全的語言的有效性時,這個問題總是出現,但實際上軟體缺陷是由於使用了 pragma 而存在的。包含導致火箭爆炸的部分的實際原始碼片段可供公眾透過美國宇航局獲取,並且仍然可以在這些日子裡線上找到,遠在爆炸成為新聞之後。也許它仍然是一個禁忌,因為它僅僅是 Ada 確實有缺陷的“證據”。

正如我提到的,它是由於錯誤使用 pragma 造成的,該 pragma 對火箭的硬體系統做出了錯誤的假設,因為它是從之前具有完全不同硬體的火箭移植過來的。

此外,Ada 中的 pragma 是特定於編譯器的,而不是語言的一部分。語言沒有對 pragma 提供相同的安全保證,因為正如我之前所說,大多數 pragma 並未在標準中定義。Ada 標準規範定義了關鍵字“pragma”,但它們表示是特定於編譯器的自定義程式碼。

根據 Ada 標準,不允許在字串中使用 0xFFFE 和 0xFFFF 位元組序列。Ada 執行時會修復無效的字串。此限制會導致 UTF-8 出現錯誤。

不允許在程式文字中的任何位置使用其平面中相對程式碼位置為 16#FFFE# 或 16#FFFF# 的字元。

這些位元組序列在 UTF-8 中用於表示表情符號。

用 Ada 編寫的程式通常需要一個帶有自己的型別和函式的附加執行時。該執行時可能有一部分是用 Ada(繫結)編寫的,而另一部分是用 C 等其他語言編寫的。

這正是 Pollen 發揮作用的地方。Pollen 可用作 Ada 的執行時,用於訪問現代作業系統功能、外部庫或 Ada 執行時未涵蓋的其他功能。

以下是 Ada 執行時未涵蓋的一些很好的功能示例

  • UTF-8;
  • 記憶體流;
  • 資料庫支援;
  • 現代壓縮演算法;
  • 現代密碼演算法;
  • 網路(Berkeley 套接字)。

如果用作執行時,Pollen 將為 Ada 提供所有這些功能。

獨立 Ada 程式碼

[編輯 | 編輯原始碼]

建立作為作業系統或微控制器上的主要程式碼使用的獨立 Ada 程式碼比在 C 中建立獨立程式碼要複雜得多。

建立獨立 Ada 程式碼更加困難,因為 Ada 需要執行時才能編譯和執行。但要理解的重要一點是,Pollen 應該遵循以下示例:將執行時程式碼作為獨立程式碼在裸機上執行的語言所需的所有執行時程式碼都包含在標準中,並且公開可用的文件,以便核心開發人員可以有效地使用該語言。

Ada 執行時是用 Ada 本身編寫的,並使用 pragma Import 指令呼叫 C 程式碼來執行依賴於語言未提供的安全功能的程式碼,例如

  • 指標和指標運算;
  • 異常處理;
  • 任務;
  • 延遲;
  • 陣列邊界檢查;
  • 等等。

這些都是跨編譯器、作業系統和硬體可移植的。執行 Ada 程式碼的硬體不一定需要提供所有這些功能,因此並非每個功能都必須在特定硬體或作業系統上存在。所有這些功能都在 Ada 語言規範標準中定義。

Ada 程式碼通常依賴於編譯器生成的程式碼,例如用於檢查指標解引用時的空值檢查的程式碼。但是,這些運算子(例如此例中的解引用運算子)可以手動覆蓋。

這些自動生成的程式碼通常不會對低階開發人員構成危險,因為它們呼叫了 Ada.Exceptions 包的異常處理程式。如果程式設計師想支援指標解引用和比較運算子的空值檢查,只需要在正在建立的作業系統的核心的 Ada 執行時建立該包上的適當處理程式。

整數溢位檢查

[編輯 | 編輯原始碼]

溢位檢查由 Ada 執行時透過使用運算子過載完成,就像面向物件的程式碼一樣。

function "+" (Left, Right : Integer) return Integer;
pragma Import (C, "+", "my_overflow_checking_function");

Ada 語言沒有提供型別。它們都是由執行時定義的,溢位檢查通常被指定為編譯器的內在函式,帶有

pragma Import (Intrinsic, "+");

全域性分配器

[編輯 | 編輯原始碼]

全域性分配器由 Root_Storage_Pool 型別及其方法的定義建立,也像面向物件的程式碼一樣。

5
with Ada.Finalization;
with System.Storage_Elements;
package System.Storage_Pools is
    pragma Preelaborate(System.Storage_Pools);
6/2
{AI95-00161-01}     type Root_Storage_Pool is
        abstract new Ada.Finalization.Limited_Controlled with private;
    pragma Preelaborable_Initialization(Root_Storage_Pool);
7
    procedure Allocate(
      Pool : in out Root_Storage_Pool;
      Storage_Address : out Address;
      Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
      Alignment : in Storage_Elements.Storage_Count) is abstract;
8
    procedure Deallocate(
      Pool : in out Root_Storage_Pool;
      Storage_Address : in Address;
      Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
      Alignment : in Storage_Elements.Storage_Count) is abstract;
9
    function Storage_Size(Pool : Root_Storage_Pool)
        return Storage_Elements.Storage_Count is abstract;
10
private
   ... -- not specified by the language
end System.Storage_Pools;

從 C 呼叫 Ada 程式碼

[編輯 | 編輯原始碼]

建立您的 Ada 原始檔,並使用 GNAT 編譯器 gcc 將它們編譯為目標檔案。例如,假設您有兩個名為 mypackage.ads 和 mypackage.adb 的 Ada 原始檔,並且您想將它們編譯為目標檔案

gcc -c mypackage.adb

建立一個 C 原始檔,它將呼叫 Ada 庫函式。在本例中,假設您想呼叫一個名為 my_ada_function 的函式,該函式在 mypackage.ads 中定義。C 原始檔可能如下所示

#include <stdio.h>
#include <stdlib.h>;
#include "mypackage.h"

int main(int argc, char** argv) {
    int result = my_ada_function();
    printf("Result: %d\n", result);
    return 0;
}

建立一個 C 標頭檔案 (mypackage.h),宣告您要呼叫的 Ada 函式。在本例中,標頭檔案可能如下所示

#ifndef MYPACKAGE_H
#define MYPACKAGE_H

#ifdef __cplusplus
extern "C" {
#endif

int my_ada_function();

#ifdef __cplusplus
}
#endif

#endif /* MYPACKAGE_H */

使用 GNAT 編譯器為您的 Ada 包生成 C 繫結

gnatbind -Llibada.a -static -I. -C mypackage

使用 C 編譯器編譯 C 原始檔,並將其與 Ada 庫檔案和 C 繫結檔案連結

#!/bin/bash -e
gcc -c mypackage.adb.c
gcc -c main.c
gcc -o myprogram main.o mypackage.o -L. -lada

Ada 標準庫使用寬字元處理 Unicode。Interfaces.C.Strings 包不支援 UTF-8 或寬字元編碼。明智的做法是在 C 端而不是 Ada 端包含字元轉碼函式。這些函式應該將 Unicode 字串轉換為 Ada Wide_String 及類似字串。

with Interfaces.C;
use Interfaces.C;
with Interfaces.C.Strings;
use Interfaces.C.Strings;

package body Pollen.Strings is

   --  ...

   function Value (Input : chars_ptr)
      return Wide_String
   is
      function To_Ada_Encoding (Input : chars_ptr; Output : Wide_String) return int;
      pragma Import (C, To_Ada_Encoding, "tmpl_to_ada_encoding");

      Output : Wide_String (1 .. Strlen (Input));
      Return_Code : int;
   begin
      Return_code := To_Ada_Encoding (Input, Output);
      if Return_Code /= 0 then
         throw Program_Error with "Unable to convert C string to Pollen string.";
      endif;
      return Output;
   end Value;

   --  ...

end Pollen.Strings;
  • GNU libjit (libgccjit-10-dev)
  • LLVM 位元組碼

Kaleidoscope 是一個使用 LLVM 程式碼生成的 JIT 語言示例。

LLVM 程式碼生成

[編輯 | 編輯原始碼]

llvm-config 類似於 pkg-config,但它專門用於 LLVM 程式碼生成。llvm-config 必須在 $PATH 中才能工作。預計只有一個與該 llvm-config 相關的 llvm 工具鏈。該工具鏈負責程式碼生成,並且可能具有不同的體系結構作為目標。

使用 llvm-dev 生成位碼的步驟

LLVM_CFLAGS=$(shell llvm-config --cflags)
LLVM_LDFLAGS=$(shell llvm-config --ldflags --libs)
  • 建立一個新的 Pollen 外掛
  • 新增 CFLAGS 和 LDFLAGS llvm-config
  • 建立模組;
  • 新增程式碼;
  • 新增基本塊;
  • 將基本塊追加到記憶體中的程式;
  • 將位碼寫入 HDD 或 SSD 上的目標檔案或 BC 檔案。

歡迎對 Pollen 專案的貢獻。

您可以建立一個 bash 指令碼,自動插入您的 Git 使用者名稱和密碼。使用簡單的 HTTP 身份驗證將程式碼推送到 Git 儲存庫。

#!/bin/bash -e

git push https://mygithubuser:ghp_mytoken@github.com/ativarsoft/pollen-lang.git

Pollen 需要執行原生代碼,因此典型的 PHP 託管伺服器將無法正常工作。截至 2023 年 3 月,很難找到免費的 CGI、Docker 和 VPS 託管。

Google 在其 Cloud Run 上提供適合個人使用的免費託管。

libpollen 執行時有一個名為 hello world 的函式。該函式應該從 tmpl 指令碼檔案中呼叫。libpollen 執行時是可選的,使用 Pollen 編寫的程式不需要它就能執行。

編譯器和直譯器以不同的方式獲取 "hello_world" 函式的符號。

直譯器使用 dlopen 在指令碼選擇的執行時庫中獲取符號。選擇指令碼是 "pollen" 元素上的 "lib" 屬性。

編譯器透過使用連結器獲取符號,因為從指令碼建立的目標檔案與 libpollen 連結在一起。

  • Mateus de Lima Oliveira
[編輯 | 編輯原始碼]
華夏公益教科書