350 changes: 189 additions & 161 deletions erts/emulator/beam/beam_load.c
Original file line number Diff line number Diff line change
Expand Up @@ -455,9 +455,10 @@ typedef struct {
} while (0)


static int bin_load(Process *c_p, ErtsProcLocks c_p_locks,
Eterm group_leader, Eterm* modp, byte* bytes, int unloaded_size);
static void init_state(LoaderState* stp);
static int bin_prep(LoaderState *state, Eterm* modp, byte* bytes, int unloaded_size);
static int bin_load_prep(LoaderState *state, Process *c_p, Eterm group_leader, ErtsProcLocks c_p_locks, Eterm* modp);
static LoaderState* init_state(void);
static void free_state(LoaderState* stp);
static int insert_new_code(Process *c_p, ErtsProcLocks c_p_locks,
Eterm group_leader, Eterm module,
BeamInstr* code, Uint size, BeamInstr catches);
Expand Down Expand Up @@ -551,38 +552,68 @@ define_file(LoaderState* stp, char* name, int idx)
}

int
erts_load_module(Process *c_p,
ErtsProcLocks c_p_locks,
Eterm group_leader, /* Group leader or NIL if none. */
Eterm* modp, /*
* Module name as an atom (NIL to not check).
* On return, contains the actual module name.
*/
byte* code, /* Points to the code to load */
int size) /* Size of code to load. */
erts_prep_module(Eterm* mod, byte* code, int size, void** state)
{
ErlDrvBinary* bin;
int result;

*state = init_state();
if (size >= 4 && code[0] == 'F' && code[1] == 'O' &&
code[2] == 'R' && code[3] == '1') {
/*
* The BEAM module is not compressed.
*/
result = bin_load(c_p, c_p_locks, group_leader, modp, code, size);
result = bin_prep((LoaderState*)*state, mod, code, size);
} else {
/*
* The BEAM module is compressed (or possibly invalid/corrupted).
*/
if ((bin = (ErlDrvBinary *) erts_gzinflate_buffer((char*)code, size)) == NULL) {
return -1;
result = -1;
goto prep_error;
}
result = bin_load(c_p, c_p_locks, group_leader, modp,
(byte*)bin->orig_bytes, bin->orig_size);
result = bin_prep((LoaderState*)*state, mod, (byte*)bin->orig_bytes, bin->orig_size);
driver_free_binary(bin);
}
prep_error:
if (result < 0) {
free_state(*state);
*state = NULL;
}
return result;
}

int
erts_load_prep_module(Process *c_p, ErtsProcLocks c_p_locks, Eterm group_leader, Eterm* modp, void **state)
{
int result;
if (*state == NULL) {
return -1;
}
result = bin_load_prep((LoaderState*)*state, c_p, c_p_locks, group_leader, modp);
free_state(*state);
*state = NULL;
return result;
}

int
erts_load_module(Process *c_p,
ErtsProcLocks c_p_locks,
Eterm group_leader, /* Group leader or NIL if none. */
Eterm* modp, /*
* Module name as an atom (NIL to not check).
* On return, contains the actual module name.
*/
byte* code, /* Points to the code to load */
int size) /* Size of code to load. */
{
void *state = NULL;
int result;
result = erts_prep_module(modp, code, size, &state);
if (result < 0) {
return result;
}
return erts_load_prep_module(c_p, c_p_locks, group_leader, modp, &state);
}
/* #define LOAD_MEMORY_HARD_DEBUG 1*/

#if defined(LOAD_MEMORY_HARD_DEBUG) && defined(DEBUG)
Expand All @@ -597,15 +628,11 @@ extern void check_allocated_block(Uint type, void *blk);
#endif

static int
bin_load(Process *c_p, ErtsProcLocks c_p_locks,
Eterm group_leader, Eterm* modp, byte* bytes, int unloaded_size)
bin_prep(LoaderState *state, Eterm* modp, byte* bytes, int unloaded_size)
{
LoaderState state;
int rval = -1;

init_state(&state);
state.module = *modp;
state.group_leader = group_leader;
state->module = *modp;

/*
* Scan the IFF file.
Expand All @@ -616,83 +643,94 @@ bin_load(Process *c_p, ErtsProcLocks c_p_locks,
#endif

CHKALLOC();
CHKBLK(ERTS_ALC_T_CODE,state.code);
state.file_name = "IFF header for Beam file";
state.file_p = bytes;
state.file_left = unloaded_size;
if (!scan_iff_file(&state, chunk_types, NUM_CHUNK_TYPES, NUM_MANDATORY)) {
CHKBLK(ERTS_ALC_T_CODE,state->code);
state->file_name = "IFF header for Beam file";
state->file_p = bytes;
state->file_left = unloaded_size;
if (!scan_iff_file(state, chunk_types, NUM_CHUNK_TYPES, NUM_MANDATORY)) {
goto load_error;
}

/*
* Read the header for the code chunk.
*/

CHKBLK(ERTS_ALC_T_CODE,state.code);
define_file(&state, "code chunk header", CODE_CHUNK);
if (!read_code_header(&state)) {
CHKBLK(ERTS_ALC_T_CODE,state->code);
define_file(state, "code chunk header", CODE_CHUNK);
if (!read_code_header(state)) {
goto load_error;
}

/*
* Initialize code area.
*/
state.code_buffer_size = erts_next_heap_size(2048 + state.num_functions, 0);
state.code = (BeamInstr *) erts_alloc(ERTS_ALC_T_CODE,
sizeof(BeamInstr) * state.code_buffer_size);

state.code[MI_NUM_FUNCTIONS] = state.num_functions;
state.ci = MI_FUNCTIONS + state.num_functions + 1;
state->code_buffer_size = erts_next_heap_size(2048 + state->num_functions, 0);
state->code = (BeamInstr *) erts_alloc(ERTS_ALC_T_CODE,
sizeof(BeamInstr) * state->code_buffer_size);

state.code[MI_ATTR_PTR] = 0;
state.code[MI_ATTR_SIZE] = 0;
state.code[MI_ATTR_SIZE_ON_HEAP] = 0;
state.code[MI_COMPILE_PTR] = 0;
state.code[MI_COMPILE_SIZE] = 0;
state.code[MI_COMPILE_SIZE_ON_HEAP] = 0;
state.code[MI_NUM_BREAKPOINTS] = 0;
state->code[MI_NUM_FUNCTIONS] = state->num_functions;
state->ci = MI_FUNCTIONS + state->num_functions + 1;

state->code[MI_ATTR_PTR] = 0;
state->code[MI_ATTR_SIZE] = 0;
state->code[MI_ATTR_SIZE_ON_HEAP] = 0;
state->code[MI_COMPILE_PTR] = 0;
state->code[MI_COMPILE_SIZE] = 0;
state->code[MI_COMPILE_SIZE_ON_HEAP] = 0;
state->code[MI_NUM_BREAKPOINTS] = 0;

/*
* Read the atom table.
*/

CHKBLK(ERTS_ALC_T_CODE,state.code);
define_file(&state, "atom table", ATOM_CHUNK);
if (!load_atom_table(&state)) {
CHKBLK(ERTS_ALC_T_CODE,state->code);
define_file(state, "atom table", ATOM_CHUNK);
if (!load_atom_table(state)) {
goto load_error;
}

/*
* Read the import table.
* Read the literal table.
*/

CHKBLK(ERTS_ALC_T_CODE,state.code);
define_file(&state, "import table", IMP_CHUNK);
if (!load_import_table(&state)) {
goto load_error;
CHKBLK(ERTS_ALC_T_CODE,state->code);
if (state->chunks[LITERAL_CHUNK].size > 0) {
define_file(state, "literals table (constant pool)", LITERAL_CHUNK);
if (!read_literal_table(state)) {
goto load_error;
}
}
rval = 0;
load_error:
return rval;
}

static int
bin_load_prep(LoaderState *state, Process *c_p, Eterm group_leader, ErtsProcLocks c_p_locks, Eterm* modp)
{
int rval = -1;

state->group_leader = group_leader;

/*
* Read the lambda (fun) table.
* Read the import table.
*/

CHKBLK(ERTS_ALC_T_CODE,state.code);
if (state.chunks[LAMBDA_CHUNK].size > 0) {
define_file(&state, "lambda (fun) table", LAMBDA_CHUNK);
if (!read_lambda_table(&state)) {
goto load_error;
}
CHKBLK(ERTS_ALC_T_CODE,state->code);
define_file(state, "import table", IMP_CHUNK);
if (!load_import_table(state)) {
goto load_error;
}

/*
* Read the literal table.
* Read the lambda (fun) table.
*/

CHKBLK(ERTS_ALC_T_CODE,state.code);
if (state.chunks[LITERAL_CHUNK].size > 0) {
define_file(&state, "literals table (constant pool)", LITERAL_CHUNK);
if (!read_literal_table(&state)) {
CHKBLK(ERTS_ALC_T_CODE,state->code);
if (state->chunks[LAMBDA_CHUNK].size > 0) {
define_file(state, "lambda (fun) table", LAMBDA_CHUNK);
if (!read_lambda_table(state)) {
goto load_error;
}
}
Expand All @@ -701,15 +739,15 @@ bin_load(Process *c_p, ErtsProcLocks c_p_locks,
* Load the code chunk.
*/

CHKBLK(ERTS_ALC_T_CODE,state.code);
state.file_name = "code chunk";
state.file_p = state.code_start;
state.file_left = state.code_size;
if (!load_code(&state)) {
CHKBLK(ERTS_ALC_T_CODE,state->code);
state->file_name = "code chunk";
state->file_p = state->code_start;
state->file_left = state->code_size;
if (!load_code(state)) {
goto load_error;
}
CHKBLK(ERTS_ALC_T_CODE,state.code);
if (!freeze_code(&state)) {
CHKBLK(ERTS_ALC_T_CODE,state->code);
if (!freeze_code(state)) {
goto load_error;
}

Expand All @@ -718,100 +756,106 @@ bin_load(Process *c_p, ErtsProcLocks c_p_locks,
* Read and validate the export table. (This must be done after
* loading the code, because it contains labels.)
*/
CHKBLK(ERTS_ALC_T_CODE,state.code);
define_file(&state, "export table", EXP_CHUNK);
if (!read_export_table(&state)) {

CHKBLK(ERTS_ALC_T_CODE,state->code);
define_file(state, "export table", EXP_CHUNK);
if (!read_export_table(state)) {
goto load_error;
}

/*
* Ready for the final touch: fixing the export table entries for
* exported and imported functions. This can't fail.
*/
CHKBLK(ERTS_ALC_T_CODE,state.code);
rval = insert_new_code(c_p, c_p_locks, state.group_leader, state.module,
state.code, state.loaded_size, state.catches);

CHKBLK(ERTS_ALC_T_CODE,state->code);
rval = insert_new_code(c_p, c_p_locks, state->group_leader, state->module,
state->code, state->loaded_size, state->catches);
if (rval < 0) {
goto load_error;
}
CHKBLK(ERTS_ALC_T_CODE,state.code);
final_touch(&state);
CHKBLK(ERTS_ALC_T_CODE,state->code);
final_touch(state);

/*
* Loading succeded.
*/
CHKBLK(ERTS_ALC_T_CODE,state.code);
CHKBLK(ERTS_ALC_T_CODE,state->code);
#if defined(LOAD_MEMORY_HARD_DEBUG) && defined(DEBUG)
erts_fprintf(stderr,"Loaded %T\n",*modp);
#if 0
debug_dump_code(state.code,state.ci);
debug_dump_code(state->code,state->ci);
#endif
#endif
rval = 0;
state.code = NULL; /* Prevent code from being freed. */
*modp = state.module;
state->code = NULL; /* Prevent code from being freed. */
*modp = state->module;

/*
* If there is an on_load function, signal an error to
* indicate that the on_load function must be run.
*/
if (state.on_load) {
if (state->on_load) {
rval = -5;
}

load_error:
if (state.code != 0) {
erts_free(ERTS_ALC_T_CODE, state.code);
return rval;
}

static void
free_state(LoaderState *state)
{
if (state->code != 0) {
erts_free(ERTS_ALC_T_CODE, state->code);
}
if (state.labels != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.labels);
if (state->labels != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->labels);
}
if (state.atom != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.atom);
if (state->atom != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->atom);
}
if (state.import != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.import);
if (state->import != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->import);
}
if (state.export != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.export);
if (state->export != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->export);
}
if (state.lambdas != state.def_lambdas) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.lambdas);
if (state->lambdas != state->def_lambdas) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->lambdas);
}
if (state.literals != NULL) {
if (state->literals != NULL) {
int i;
for (i = 0; i < state.num_literals; i++) {
if (state.literals[i].heap != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.literals[i].heap);
for (i = 0; i < state->num_literals; i++) {
if (state->literals[i].heap != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->literals[i].heap);
}
}
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.literals);
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->literals);
}
while (state.literal_patches != NULL) {
LiteralPatch* next = state.literal_patches->next;
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.literal_patches);
state.literal_patches = next;
while (state->literal_patches != NULL) {
LiteralPatch* next = state->literal_patches->next;
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->literal_patches);
state->literal_patches = next;
}
while (state.string_patches != NULL) {
StringPatch* next = state.string_patches->next;
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.string_patches);
state.string_patches = next;
while (state->string_patches != NULL) {
StringPatch* next = state->string_patches->next;
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->string_patches);
state->string_patches = next;
}
while (state.genop_blocks) {
GenOpBlock* next = state.genop_blocks->next;
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.genop_blocks);
state.genop_blocks = next;
while (state->genop_blocks) {
GenOpBlock* next = state->genop_blocks->next;
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state->genop_blocks);
state->genop_blocks = next;
}

return rval;
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state);
}


static void
init_state(LoaderState* stp)
static LoaderState*
init_state(void)
{
LoaderState* stp = erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(LoaderState));
stp->function = THE_NON_VALUE; /* Function not known yet */
stp->arity = 0;
stp->specific_op = -1;
Expand All @@ -835,6 +879,7 @@ init_state(LoaderState* stp)
stp->string_patches = 0;
stp->may_load_nif = 0;
stp->on_load = 0;
return stp;
}

static int
Expand Down Expand Up @@ -5200,7 +5245,7 @@ patch_funentries(Eterm Patchlist)
Eterm
erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
{
LoaderState state;
LoaderState* state;
BeamInstr Funcs;
BeamInstr Patchlist;
Eterm* tp;
Expand All @@ -5219,10 +5264,10 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
Uint size;

/*
* Must initialize state.lambdas here because the error handling code
* Must initialize state->lambdas here because the error handling code
* at label 'error' uses it.
*/
init_state(&state);
state = init_state();

if (is_not_atom(Mod)) {
goto error;
Expand Down Expand Up @@ -5262,31 +5307,31 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
* Scan the Beam binary and read the interesting sections.
*/

state.file_name = "IFF header for Beam file";
state.file_p = bytes;
state.file_left = size;
state.module = Mod;
state.group_leader = p->group_leader;
state.num_functions = n;
if (!scan_iff_file(&state, chunk_types, NUM_CHUNK_TYPES, NUM_MANDATORY)) {
state->file_name = "IFF header for Beam file";
state->file_p = bytes;
state->file_left = size;
state->module = Mod;
state->group_leader = p->group_leader;
state->num_functions = n;
if (!scan_iff_file(state, chunk_types, NUM_CHUNK_TYPES, NUM_MANDATORY)) {
goto error;
}
define_file(&state, "code chunk header", CODE_CHUNK);
if (!read_code_header(&state)) {
define_file(state, "code chunk header", CODE_CHUNK);
if (!read_code_header(state)) {
goto error;
}
define_file(&state, "atom table", ATOM_CHUNK);
if (!load_atom_table(&state)) {
define_file(state, "atom table", ATOM_CHUNK);
if (!load_atom_table(state)) {
goto error;
}
define_file(&state, "export table", EXP_CHUNK);
if (!stub_read_export_table(&state)) {
define_file(state, "export table", EXP_CHUNK);
if (!stub_read_export_table(state)) {
goto error;
}

if (state.chunks[LAMBDA_CHUNK].size > 0) {
define_file(&state, "lambda (fun) table", LAMBDA_CHUNK);
if (!read_lambda_table(&state)) {
if (state->chunks[LAMBDA_CHUNK].size > 0) {
define_file(state, "lambda (fun) table", LAMBDA_CHUNK);
if (!read_lambda_table(state)) {
goto error;
}
}
Expand All @@ -5296,8 +5341,8 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
*/

code_size = ((WORDS_PER_FUNCTION+1)*n + MI_FUNCTIONS + 2) * sizeof(BeamInstr);
code_size += state.chunks[ATTR_CHUNK].size;
code_size += state.chunks[COMPILE_CHUNK].size;
code_size += state->chunks[ATTR_CHUNK].size;
code_size += state->chunks[COMPILE_CHUNK].size;
code = erts_alloc_fnf(ERTS_ALC_T_CODE, code_size);
if (!code) {
goto error;
Expand Down Expand Up @@ -5387,12 +5432,12 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
*/

info = (byte *) fp;
info = stub_copy_info(&state, ATTR_CHUNK, info,
info = stub_copy_info(state, ATTR_CHUNK, info,
code+MI_ATTR_PTR, code+MI_ATTR_SIZE_ON_HEAP);
if (info == NULL) {
goto error;
}
info = stub_copy_info(&state, COMPILE_CHUNK, info,
info = stub_copy_info(state, COMPILE_CHUNK, info,
code+MI_COMPILE_PTR, code+MI_COMPILE_SIZE_ON_HEAP);
if (info == NULL) {
goto error;
Expand All @@ -5414,21 +5459,16 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)

fp = code + ci;
for (i = 0; i < n; i++) {
stub_final_touch(&state, fp);
stub_final_touch(state, fp);
fp += WORDS_PER_FUNCTION;
}

if (patch_funentries(Patchlist)) {
erts_free_aligned_binary_bytes(temp_alloc);
if (state.lambdas != state.def_lambdas) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.lambdas);
}
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.labels);
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.atom);
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.export);
if (bin != NULL) {
driver_free_binary(bin);
}
free_state(state);
return Mod;
}

Expand All @@ -5437,23 +5477,11 @@ erts_make_stub_module(Process* p, Eterm Mod, Eterm Beam, Eterm Info)
if (code != NULL) {
erts_free(ERTS_ALC_T_CODE, code);
}
if (state.labels != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.labels);
}
if (state.lambdas != state.def_lambdas) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.lambdas);
}
if (state.atom != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.atom);
}
if (state.export != NULL) {
erts_free(ERTS_ALC_T_LOADER_TMP, (void *) state.export);
}
if (bin != NULL) {
driver_free_binary(bin);
}
free_state(state);


BIF_ERROR(p, BADARG);
}

Expand Down
4 changes: 4 additions & 0 deletions erts/emulator/beam/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -860,6 +860,10 @@ void erts_system_profile_clear(Process *c_p);
/* beam_load.c */
int erts_load_module(Process *c_p, ErtsProcLocks c_p_locks,
Eterm group_leader, Eterm* mod, byte* code, int size);
int erts_prep_module(Eterm* mod, byte* code, int size,
void** state);
int erts_load_prep_module(Process *c_p, ErtsProcLocks c_p_locks,
Eterm group_leader, Eterm* modp, void** state);
void init_load(void);
BeamInstr* find_function_from_pc(BeamInstr* pc);
Eterm erts_module_info_0(Process* p, Eterm module);
Expand Down