diff --git a/.gitignore b/.gitignore index 0657bc7..b0d82b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin /a +/src/ciabatta.c a.out *.a *.so diff --git a/build.lua b/build.lua index 073daff..73ec5fb 100755 --- a/build.lua +++ b/build.lua @@ -2,6 +2,7 @@ local path = require 'path' local argparse = require 'argparse' +local mf_parser = require 'scripts.manifest-parser' -- Parse command line arguments local parser = argparse('build.lua', 'Ciabatta build script') @@ -109,14 +110,16 @@ tinyrt_iface_hdr:write('// This file is AUTO-GENERATED\n') tinyrt_iface_hdr:write('// See tinyrt.mf\n') tinyrt_iface_hdr:write('\n') n = 1 +tinyrt_apis = {} for line in io.lines(tinyrt_manifest_path) do if line:len() ~= 0 and line:sub(1,1) ~= '#' and line:gsub('%s+', '') ~= '' then local line_it = line:gmatch('[_a-zA-Z0-9]+') - local api_name = line_it():upper() + local api_name = line_it() local has_impl = line_it() if has_impl == '0' or has_impl == '1' then - local api_define = '#define ' .. api_name .. ' '..has_impl..'\n' + local api_define = '#define ' .. (api_name:upper()) .. ' '..has_impl..'\n' tinyrt_iface_hdr:write(api_define) + table.insert(tinyrt_apis, api_name) else print('SYNTAX ERROR AT LINE '..n..': Expected 1 or 0 for the value') end @@ -125,6 +128,50 @@ for line in io.lines(tinyrt_manifest_path) do n = n+1 end io.close(tinyrt_iface_hdr) +-- Parse manifest and generate ciabatta.c +local function has_value(tab, val) + for index, value in ipairs(tab) do + if value == val then + return true + end + end + + return false +end +print('==> Generating ciabatta.c') +cia_h = io.open(path.join('src', 'ciabatta.c'), 'wb') +mf = parse_mf(path.join('src', 'library.mf')) +cia_h:write('\n') +cia_h:write('// THIS FILE IS AUTO-GENERATED. SEE library.mf FOR DETAILS\n') +cia_h:write('\n// global includes\n') +for index,include in ipairs(mf.includes) do + cia_h:write('#include <'..include..'>\n') +end +for index,decl_platform in ipairs(mf.platforms) do + if decl_platform.name == platform then + cia_h:write(('\n// platform %s\n'):format(decl_platform.name)) + for index,include in ipairs(decl_platform.includes) do + cia_h:write('#include "'..include..'"\n') + end + end +end +for index, api in ipairs(mf.apis) do + supported = true + for index, dep in ipairs(api.deps) do + if not has_value(tinyrt_apis, dep) then + supported = false + end + end + if supported then + cia_h:write(('\n// module %s\n'):format(api.name)) + print(' - Exporting module: ' .. api.name) + for index, include in ipairs(api.includes) do + cia_h:write('#include "'..include..'"\n') + end + end +end +io.close(cia_h) + -- Figure out compiler flags local cflags = table.concat(compiler_flags, ' ')..' '.. @@ -165,12 +212,12 @@ end path.mkdir('lib') path.mkdir('bin') -assemble('src/linux/crt_entry.asm', 'bin/crt_entry.o') -compile({'src/linux/crt_ctors.c'}, 'bin/crt_ctors.o', '-fpic -c') +assemble('src/linux/crt-entry.asm', 'bin/crt-entry.o') +compile({'src/linux/crt-ctors.c'}, 'bin/crt-ctors.o', '-fpic -c') compile({'src/ciabatta.c'}, 'bin/ciabatta.o', '-fpic -c') if library_type == 'static' then - archive({'bin/ciabatta.o', 'bin/crt_ctors.o', 'bin/crt_entry.o'}, 'lib/'..ciabatta_lib) + archive({'bin/ciabatta.o', 'bin/crt-ctors.o', 'bin/crt-entry.o'}, 'lib/'..ciabatta_lib) elseif library_type == 'shared' then print('SHARED OBJECTS NOT SUPPORTED YET') os.exit(1) diff --git a/include/cia_definitions.h b/include/cia-def.h similarity index 100% rename from include/cia_definitions.h rename to include/cia-def.h diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100644 index 0000000..a0a5ac5 --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,11 @@ + +#pragma once + +#include + +int atexit(void (*func)(void)); +int at_quick_exit(void (*func)(void)); +noreturn void abort(void); +noreturn void exit(int code); +noreturn void _Exit(int code); +noreturn void quick_exit(int code); \ No newline at end of file diff --git a/scripts/manifest-parser.lua b/scripts/manifest-parser.lua new file mode 100755 index 0000000..bb9e633 --- /dev/null +++ b/scripts/manifest-parser.lua @@ -0,0 +1,265 @@ +#!/usr/bin/env lua + +local path = require 'path' + +levels = {} +parser_obj = {} +parser_cur_ind = 0 +parser_ind_lv = 0 + +-- Tokens +Token = {} +function Token.new(kind, value) + return {kind = kind, value = value} +end +function Token.keyword(kw) return Token.new('keyword', kw) end +function Token.string(str) return Token.new('string', str) end +function Token.lparen() return Token.new('(', nil) end +function Token.rparen() return Token.new(')', nil) end +function Token.lbrace() return Token.new('{', nil) end +function Token.rbrace() return Token.new('}', nil) end +function Token.colon() return Token.new(':', nil) end +function Token.comma() return Token.new(',', nil) end +function Token.eof() return Token.new('eof', nil) end + +Parser = { + file = nil, + filename = nil, + row = 0, + col = 0, + ind_spaces = 0, + ind_levels = {0}, + c = nil, -- Last Read Character + t = nil, -- Last Read Token +} +-- Character functions +function Parser.char_next(parser, c) + char = parser.file:read(1) + parser.col = parser.col + 1 + if char == '\n' then + parser.row = parser.row + 1 + parser.col = 0 + end + parser.c = char +end +function Parser.char(parser) + return parser.c +end +function Parser.char_is(parser, pattern) + match = parser:char():match(pattern) + if match then + return true + else + return false + end +end +function Parser.char_match(parser, pattern) + if parser:char_is(pattern) then + parser:char_next() + return true + end + return false +end +-- Token functions +function Parser.token(parser) + return parser.t +end +function Parser.token_next(parser) + -- Skip over the whitespace + while true do + if parser.c == nil then + parser.t = Token.eof() + return + elseif parser:char_match('#') then + while not parser:char_match('\n') do + parser:char_next() + end + elseif parser:char_is('%s') then + parser:char_next() + else + break + end + end + -- print(1+parser.row, 1+parser.col, parser:char()) + -- Keyword/identifier + if parser:char_is('[%a-_]') then + ident = '' + while parser:char_is('[%a%d-_]') do + ident = ident .. parser:char() + parser:char_next() + end + parser.t = Token.keyword(ident) + return + -- String + elseif parser:char_match('"') then + string = '' + while not parser:char_match('"') do + string = string .. parser:char() + parser:char_next() + end + parser.t = Token.string(string) + return + -- Single-char tokens + elseif parser:char_match('%(') then + parser.t = Token.lparen() + elseif parser:char_match('%)') then + parser.t = Token.rparen() + elseif parser:char_match('{') then + parser.t = Token.lbrace() + elseif parser:char_match('}') then + parser.t = Token.rbrace() + elseif parser:char_match(':') then + parser.t = Token.colon() + elseif parser:char_match(',') then + parser.t = Token.comma() + end +end +function Parser.token_is(parser, kind) + return parser:token().kind == kind +end +function Parser.token_kw_is(parser, name) + return parser:token().kind == 'keyword' and parser:token().value == name +end +function Parser.token_match(parser, kind) + if parser:token().kind == kind then + parser:token_next() + return true + end + return false +end +function Parser.token_kw_match(parser, name) + if parser:token().kind == 'keyword' and parser:token().value == name then + parser:token_next() + return true + end + return false +end + +function Parser.new(filename) + file = io.open(filename, 'rb') + parser = Parser + parser.file = file + parser.filename = filename + parser:char_next() + parser:token_next() + return parser; +end + +function parse_mf(mf_path) + parser = Parser.new(mf_path) + includes = {} + platforms = {} + apis = {} + while parser:token().kind ~= 'eof' do + if parser:token().kind ~= 'keyword' then + print(('%s(%d, %d): Expected keyword'):format(parser.filename, 1+parser.row, 1+parser.col)) + end + -- TODO: add error handling + if parser:token_kw_match('include') then + parser:token_match('{') + while not parser:token_match('}') do + inc = parser:token().value + table.insert(includes, inc) + parser:token_next() + parser:token_match(',') + end + elseif parser:token_kw_match 'platform' then + platform_name = parser:token().value + parser:token_next() + parser:token_match('(') + platform_path = parser:token().value + if platform_path:sub(1,1) == '/' then + platform_path = platform_path:sub(2, -1) + end + parser:token_next() + parser:token_match(')') + parser:token_match('{') + platform_includes = {} + while not parser:token_match('}') do + if parser:token_kw_match('tinyrt') then + table.insert(platform_includes, path.join(platform_path, 'tinyrt.iface.h')) + table.insert(platform_includes, 'tinyrt.h') + parser:token_match('{') + while not parser:token_match('}') do + inc = parser:token().value + table.insert(platform_includes, path.join(platform_path, inc)) + parser:token_next() + end + else + inc = parser:token().value + table.insert(platform_includes, path.join(platform_path, inc)) + parser:token_next() + end + parser:token_match(',') + end + table.insert(platforms, { + name = platform_name, + includes = platform_includes + }) + elseif parser:token_kw_match 'api' then + api_name = parser:token().value + parser:token_next() + parser:token_match('(') + api_path = parser:token().value + if api_path:sub(1,1) == '/' then + api_path = api_path:sub(2, -1) + end + parser:token_next() + parser:token_match(')') + parser:token_match('{') + api_includes = {} + rt_deps = {} + while not parser:token_match('}') do + if parser:token_kw_match('tinyrt') then + parser:token_match('{') + while not parser:token_match('}') do + dep = parser:token().value + table.insert(rt_deps, dep) + parser:token_next() + end + else + inc = parser:token().value + table.insert(api_includes, path.join(api_path, inc)) + parser:token_next() + end + parser:token_match(',') + end + table.insert(apis, { + name = api_name, + deps = rt_deps, + includes = api_includes + }) + else + print(('%s(%d, %d): Unknown directive: %s'):format(parser.filename, 1+parser.row, 1+parser.col, parser:token().value)) + end + end + io.close(parser.file) + return { + includes = includes, + platforms = platforms, + apis = apis, + } +end + +function print_r(arr, indentLevel) + local str = "" + local indentStr = "" + if(indentLevel == nil) then + print(print_r(arr, 0)) + return + end + for i = 0, indentLevel do + indentStr = indentStr.." " + end + for index,value in pairs(arr) do + if type(value) == "table" then + str = str..indentStr..index..": \n"..print_r(value, (indentLevel + 1)) + else + str = str..indentStr..index..": "..value.."\n" + end + end + return str +end + +-- mf = parse_mf('src/library.mf') +-- print(print_r(mf, 0)) diff --git a/src/ciabatta.c b/src/ciabatta.c deleted file mode 100644 index f829f9c..0000000 --- a/src/ciabatta.c +++ /dev/null @@ -1,14 +0,0 @@ - -#include - -#if _CIA_OS_LINUX() - #include "linux/syscall.c" - #include "linux/errno.c" - #include "linux/entry.c" - // TinyRT interface - #include "linux/tinyrt.iface.h" - #include "tinyrt.h" - #include "linux/tinyrt.c" -#elif _CIA_OS_WINDOWS() - #error "Not implemented yet" -#endif diff --git a/src/impl/c11-program/program.c b/src/impl/c11-program/program.c new file mode 100644 index 0000000..e510374 --- /dev/null +++ b/src/impl/c11-program/program.c @@ -0,0 +1,54 @@ + +#define MAX_ATEXIT_HANDLERS 32 +#define MAX_AT_QUICK_EXIT_HANDLERS 32 + +static void (*atexit_handlers[MAX_ATEXIT_HANDLERS])(void); +static void (*at_quick_exit_handlers[MAX_AT_QUICK_EXIT_HANDLERS])(void); +static u64 n_atexit_handlers = 0; +static u64 n_at_quick_exit_handlers = 0; + +int atexit(void (*func)(void)) { + if(n_atexit_handlers == MAX_ATEXIT_HANDLERS) { + return MAX_ATEXIT_HANDLERS; + } + atexit_handlers[n_atexit_handlers] = func; + n_atexit_handlers += 1; + return 0; +} + +int at_quick_exit(void (*func)(void)) { + if(n_at_quick_exit_handlers == MAX_AT_QUICK_EXIT_HANDLERS) { + return MAX_AT_QUICK_EXIT_HANDLERS; + } + at_quick_exit_handlers[n_at_quick_exit_handlers] = func; + n_at_quick_exit_handlers += 1; + return 0; +} + +noreturn void abort(void) { + // TODO: Ideally do a debug trap if the process is being debugged + _Exit(-1); +} + +noreturn void exit(int code) { + for(u64 i = n_atexit_handlers-1; i-- != 0; ) { + void (*handler)(void) = atexit_handlers[i]; + handler(); + } + // TODO(bumbread): flush all the unflushed file streams + // TODO(bumbread): close all file streams and delete temporary files + rt_program_exit(code); +} + +noreturn void _Exit(int code) { + rt_program_exit(code); +} + +noreturn void quick_exit(int code) { + for(u64 i = n_at_quick_exit_handlers-1; i-- != 0; ) { + void (*handler)(void) = at_quick_exit_handlers[i]; + handler(); + } + rt_program_exit(code); +} + diff --git a/src/library.mf b/src/library.mf new file mode 100644 index 0000000..fe7b890 --- /dev/null +++ b/src/library.mf @@ -0,0 +1,48 @@ + +# Manifest file for the ciabatta build. + +# Platform sections describe available platforms and +# which files they need to include to function +# API sections describe API subsets and their TinyRT +# dependencies. If an API requires TinyRT module that +# isn't implemented by the platform that API won't be included +# in the final build + +# This file is used to auto-generate ciabatta.c + +# `include` section specifies the header files that ciabatta +# implements +# `platform` sections describe platforms, the directories +# where the source code for these platforms resides and the +# files that should be included in the build for that platform +# (relative to "src" dir) as well as the location of TinyRT +# implementation. +# `api` sections describe modules, where the implementation resides, +# the source files and the TinyRT modules, on which this module +# depends. If the platform doesn't implement one of the dependencies +# this module will not be included in the final build + +include { + "cia-def.h", + "stdlib.h" +} + +platform linux("/linux") { + "syscall.c", + "errno.c", + "entry.c", + tinyrt { + "tinyrt.c" + } +} + +platform windows("/windows") { + tinyrt {}, +} + +api c11_program("/impl/c11-program") { + "program.c", + tinyrt { + rt_api_program, + } +} diff --git a/src/linux/crt_ctors.c b/src/linux/crt-ctors.c similarity index 97% rename from src/linux/crt_ctors.c rename to src/linux/crt-ctors.c index 974e521..d814d47 100644 --- a/src/linux/crt_ctors.c +++ b/src/linux/crt-ctors.c @@ -1,5 +1,5 @@ -#include +#include // NOTE: These symbols are provided by the linker #define attribute_hidden __attribute__((__visibility__("hidden"))) diff --git a/src/linux/crt_entry.asm b/src/linux/crt-entry.asm similarity index 100% rename from src/linux/crt_entry.asm rename to src/linux/crt-entry.asm diff --git a/src/tinyrt.h b/src/tinyrt.h index 7a2dd1f..2df0990 100644 --- a/src/tinyrt.h +++ b/src/tinyrt.h @@ -1,8 +1,6 @@ #pragma once -#include - // Common errors #define RT_STATUS_OK 0 // No errors #define RT_ERROR_NOT_IMPLEMENTED -1 // Function not implemented